2-SAT問題,其實是一個邏輯互斥問題。
做了兩道裸題之后仔細想來,和小時候做過的“有兩個女生,如果A是女生,那么B一定不是女生。A和C性別相同,求A、B、C三人的性別。”幾乎是一樣的。
對於這道題我們來分析一下。
“如果A是女生,那么B一定不是女生”——A和B性別相反,假設“A為女生”為true,那么“B為女生”必定為false,於是一定有“B為男生”為true【注意是一定】。
對於這句話同理可得,假設“B為女生”為true,那么“A為男生”必定為true。
“A和C性別相同”——假設“A為女生”為true,那么“C為女生”必定為true; 假設“A為男生”為true,那么“C為男生”必定為true。
這道題的邏輯關系十分簡單,我們可以肉眼看出答案,然而對於一些邏輯關系成千上萬達到1E6數量的我們不能直觀看出,所以針對比較復雜的情況,人力想出了列表的方法。
| A | B | C
——————————
女 | | |
——————————
男 | | |
之后對於隨機選擇一個格子進行假設保證不出現互斥的情況。
如何實現的?比如我在"A男"這一格打了勾,那么必定會在“B女”,“C男”打勾,發現不滿足題意,再令A為女生重新打勾。
下面討論2-SAT的算法。
先說一些概念:
一個命題:若P則Q。
它的逆否命題:若非Q則非P。
這兩個是等價的(大多2-SAT圖是對稱的,一些不對稱的圖指定了某個條件的真假性)
一些“不同時為真”、“不同時為假”的邏輯關系都可以化成“若P則Q”的形式(“不同時為真”:若A為真則B為假,及其逆否命題 若B為真則A為假;若B為假則A為真,及其逆否命題 若A為假則B為真)
對於“若P則Q”,我們從P出發,連一條邊到Q,代表P如果為真Q一定為真。對於所有這樣的邏輯關系都建立這樣的邏輯邊,之后選取一個節點進行假設賦值,如果這個點無論真假都不滿足條件,該問題就無解。
然后解題順序:
1.建圖(把每個點拆成點A 2*i 和點B2*i+1,對於每個點A和B必定有且僅有一個為真)
2.跑2-SAT
下面放代碼模板
1 void _clean(){ 2 3 for(int i=2;i<=n*2+1;i++){ 4 g[i].clear(); 5 mark[i]=false; 6 } 7 } 8 9 bool _dfs(int u){ 10 11 if(mark[u^1])return false;//如果該點的對立面為真,該點必定為假 12 if(mark[u])return true;//如果該點之前掃過,為真,那么直接返回 13 mark[u]=true;//如果這個點沒討論過(A/B兩點均沒賦值),那么把該點賦為真 14 stk[++top]=u;//進棧,這個我還沒能理解,不過最后滿棧應該是top==n,棧里的都為真 15 for(int i=0;i<g[u].size();i++) 16 if(!_dfs(g[u][i]))return false;//該點為真,那么和這個點相連的每個點全必須為真,否則返回false 17 return true; 18 } 19 20 bool _Twosat(){ 21 22 _clean(); 23 top=0; 24 for(int i=2;i<=n*2+1;i+=2) 25 if(!mark[i+1] && !mark[i]){//如果該點沒討論過 26 top=0; 27 if(!_dfs(i)){假設A點為真失敗 28 while(top) 29 mark[stk[top--]]=false;//棧里元素全出棧,並賦值為假 30 if(!_dfs(i+1))return false;//如果假設B點為真也失敗,那么無解。 31 } 32 } 33 return true; 34 }
補充:
1、2-SAT問題就是一個“不能同時為真”、“若滿足狀態1就必定滿足狀態2”的邏輯問題。這一類問題的特點大多是對於每個對象可以等價成兩種互斥狀態。
2、大多的2-SAT問題是對稱的,即滿足p->q,就一定滿足-q->-p(逆否命題的互推),但也有個別例外,就是比如有一個對象與2-sat圖的其他對象關聯,但是題目已經決定了這個對象必須是某一個狀態(不能自我假設了,這個時候可以對於這個點跑一個裸的dfs,一會兒我會講到例題)
下邊講幾道2-SAT的題目(都只講建圖不放代碼了)
1、
另一篇博文里有詳細題解:http://www.cnblogs.com/L-Excalibur/p/8513386.html
2、
題目鏈接:https://cn.vjudge.net/contest/209473#problem/K
題目大意:有一堆宇航員要去完成任務,A任務非常偉大,只有能力值大於等於平均能力值的人才能完成,B任務是普通任務,只有能力值低於平均能力值的人才會去完成,C任務不限制條件,所有能力值的人都可以做。但是這堆宇航員互相有一些憎恨關系,如果兩個宇航員互相憎恨,那么就不能給他們分配同一個任務,給出一個合理方案,如果沒有合理方案輸出no solution。
解題思路:每個能力值大於等於平均值的人(以下簡稱第一類人)划分為兩種狀態a.選擇任務A,b.選擇任務C;能力值小於平均值(第二類人)划分為兩種狀態a.選擇任務B,b.選擇任務C。如果第一類人或第二類人有內部矛盾,那么如果其中一個為狀態a,另一個一定為狀態b(對稱建圖,不能同時為a和b,共四條)。如果第一類人和第二類人有矛盾,那么不能同時為b,一個為b另一個一定為a。