2 SAT學習筆記

2021-09-13 18:17:51 字數 2580 閱讀 5890

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 的拓撲序.當圖...