2-sat問題指的是,給你若干個0-1變數。並給你一些限制,讓你求滿足這些限制的可行解(字典序最小的解)。
限制的種類包括以下幾種:
1:a一定是1
2:a一定不是1
3:a,b至少有1個是1
4:a,b最多有乙個是1
5:a,b一定相同
6:a,b一定不同
一般思路是對於乙個變數,建立兩個變數(正,反)代表假設選什麼的情況。用有向邊來表達推出關係。 如果乙個正點可以直接或者間接地推出它的反點,且反點也可以推出正點,說明無論是正是反都是自相矛盾。就一定無解。即正反兩點處於同乙個強連通分量裡面,就一定無解啦啦啦。
判斷可行解:
當x
xx 所在的強連通分量的拓撲序在 ¬
x\neg x
¬x所在的強連通分量的拓撲序之後取 x
xx 為真 。(可以理解成x
xx為真一定不要推出¬
x\neg x
¬x為真)所以對於縮完點之後的新圖。拓撲排序一下,記錄下每個點和反點的拓撲序。判斷一下是否可以為真就好了。
小技巧:用tarjan找到scc
sccsc
c之後。scc
sccsc
c標號的順序就是該scc
sccsc
c拓撲序的逆序。考慮tarjan的過程,dfs樹底部的那些點已經求好了。所以標號靠前一些。
#include
using
namespace std;
const
int n =
2e6+5;
int head[n]
, nxt[n]
, v[n]
, tot;
int head2[n]
, nxt2[n]
, v2[n]
, tot2;
int dfn[n]
, low[n]
, sta[n]
, col[n]
, insta[n]
, sum, tim, top;
int rnk[n]
, ind[n]
, cnt;
int n, m;
inline
void
add(
int x,
int y)
inline
intread()
while
(ch >=
'0'&& ch <=
'9')
return x * f;
}inline
void
tarjan
(int x)
else
if(insta[y]
) low[x]
=min
(low[x]
, dfn[y]);
}if(low[x]
== dfn[x])}
}inline
void
add(
int x,
int y)
intmain()
for(
int i =
1; i <=
(n <<1)
;++i)
for(
int i =
1; i <= n;
++i)
}puts
("possible");
// tarjan to find the circle
for(
int i =
1; i <= n;
++i)
printf
("\n");
return0;
}
另一種方法:
對於每個當前不確定的變數ai,令ai=0然後沿著邊dfs訪問相連的點。
檢查如果會導致任意乙個j與j′都被選,那麼撤銷。否則令ai=0
否則令ai=1,重複2。如果還不行就無解。
繼續考慮下乙個不確定的變數
這樣的話正確性顯然,由於這裡的dfs涉及到全域性,因此複雜度是o(n(n+m))的。
一般情況下已經很優秀了,而且還可以改進:
只需要在dfs之前判斷i′能否走到i就可以省略撤銷標記的過程,所以我們可以bitset優化傳遞閉包做到o(n
+mw)
o(\frac)
o(wn+m
)預處理,然後就可以o(n+m)的dfs了。
這種做法還可以保證解的字典序,有時不失為一種不錯的方法。
規定字典序最小的方法:
dfs優先按照字典序小的來確定。列舉嘗試是否乙個點可以為1/0。每個點會導致乙個鏈式反應。如果當前情況無法滿足,就回溯撤銷。
update:
(dfs的口胡寫法)
列舉每個點的選擇情況。把它所能推出的情況全部打標記並記錄在棧裡面。如果出現了標記衝突,就把棧中的標記清空,就看看這個點的反面是否可行。如果不可行就無解,否則這個點就被確定下來了。(此處標記是不清空的,代表已經選好了)
一種情況。考慮是否存在標記不衝突,你選好了一種情況。把它所推出的scc全部變成1之後。下乙個點選什麼都不合法了(下乙個點選什麼都會恰好指向乙個它的反面的點)
2 SAT學習筆記
由對稱性解2 sat問題 2 sat解法 上面兩篇 很清楚的介紹了什麼是2 sat以及一些原理演算法 2 sat問題是圖論中乙個比較有意思的問題,重點是建圖,對於邊的意思,就是如果你選了i,就必須選j。2 sat問題有個很明顯的地方就是對於每個i,i包含兩個點,i表示選第乙個點,i 表示選第二個點,...
2 sat學習筆記
例 struct twosat x xval or y yval void add clause int x,int xv,int y,int yv void init int n bool solve return1 為什麼不需要回溯呢?因為以前定下的變數如果在一輪dfs完之後沒有判為無解,那麼以...
學習筆記 2 SAT
拆點 將每個 bool 變數拆成 0,1 兩個點.連邊 將限制條件轉化為連邊.圖是 dag 時,對於每個 bool 變數,合法點的拓撲序大於非法點.證明 若某個 bool 變數拆分成的兩個點為 u,v 若 u 為非法點,則存在一條從 u 到 v 的路徑,所以 v 的拓撲序一定大於 u 的拓撲序.當圖...