什么是2-sat問題
有n個布爾型變量xi,另外m個需要滿足的條件。每個條件都是“xi為真/假或者xj為真/假”。這句話中的“或者”意味着兩個條件中至少有一個正確。2-sat問題的目標是給每個變量賦值,使得所有的條件得到滿足。
算法的大致過程是這樣的:
構造一張有向圖G,其中每個變量拆成兩個結點2i和2i+1,分別表示xi為假和xi為真。最后要為每個變量選其中一個結點標記。
對於每個“xi為假或者xj為假"這樣的條件,我們連兩條對稱的有向邊。我們上面說過,或者意味着兩個中間至少有一個正確。所以如果xi為真的話,那么xj一定為假,所以我們從2*i+1向2*j連一條有向邊。同樣的道理,我們也從2*j+1向2*i連一條有向邊。
接下來我們逐一考慮每個沒有賦值的變量,設為xi。我們先假定它為假,然后標記結點2i,並且沿着有向邊標記所有能標記的結點。如果標記的過程中發現某個變量對應的兩個結點都被標記,則xi假這個假定不成立,需要改為xi為真,然后重新標記。
模板代碼如下
code from LRJ

1 /* 2 將每個變量拆成兩個點2i和2i+1,分別表示xi為假和xi和真 3 對於每個條件,連兩條對稱的有向邊。 4 逐一考慮沒有賦值的變量,先假定它為假,然后標記結點2i,然后沿着有向邊標記所有能標記的結點 5 如果標記的過程中 發現某個變量對應的兩個結點都被標記,則xi為假這個假定不成立,需要改xi為真 6 然后重新標記 7 */ 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 #include <iostream> 12 #include <vector> 13 14 using namespace std; 15 const int maxn=100+10; 16 struct TwoSAT{ 17 int n; 18 vector<int>G[2*maxn]; 19 bool mark[maxn*2]; 20 int S[maxn*2],c; 21 bool dfs(int x){ 22 if(mark[x^1])return false; 23 if(mark[x])return true; 24 mark[x]=true; 25 S[c++]=x; 26 for(int i=0;i<G[x].size();i++){ 27 if(!dfs(G[x][i]))return false; 28 } 29 return true; 30 } 31 void init(int n){ 32 this->n=n; 33 for(int i=0;i<n*2;i++)G[i].clear(); 34 memset(mark,0,sizeof(mark)); 35 } 36 void add_clause(int x,int xval,int y,int yval){ 37 x=x*2+xval; 38 y=y*2+yval; 39 G[x^1].push_back(y); 40 G[y^1].push_back(x); 41 } 42 43 bool solve(){ 44 for(int i=0;i<n*2;i+=2){ 45 if(!mark[i]&&!mark[i+1]){ 46 c=0; 47 if(!dfs(i)){ 48 while(c>0)mark[S[--c]]=false; 49 if(!dfs(i+1))return false; 50 } 51 } 52 } 53 return true; 54 } 55 }; 56 int main(){ 57 return 0; 58 }