給定一串布林變數,每個變數只能為真或假。要求對這些變數進行賦值,滿足布林方程。這就是2-sat問題。
我們發現每塊點都有兩種狀態(真、假),於是我們可以想到將點 \(u\) 拆分成 \(u0,u1\) ,分別表示 \(u\) 點為假、真。我們若連的邊為 \(u \to v\) ,就表示選擇 \(u\) 就必須選 \(v\) 。
如果題目給出了一種條件 \(a \and b = true\) ,那麼我們就連一條 \(a1 \to b1\) 的邊。
連邊方法:
\(a \and b = true \rightarrow (a1,b1),(b1,a1)\)
\(a \and b = false \rightarrow (a1,b0),(b1,a0)\)
\(a \or b = true \rightarrow (a0,b1),(b0,a1)\)
\(a \or b = false \rightarrow (a0,b0),(b0,a0)\)
由所構造的狀態可知,對於圖中的每乙個強連通分量,如果選擇了其中任意乙個點,那就意味著這個強連通分量中的所有點都要選。顯然 \(x0,x1\) 不可以同時選,由此可以判斷有無解。
由連邊的方式可以得知,我們對於每個點的兩種狀態,選擇拓撲序大的,捨棄掉另乙個。
小技巧:這裡可以不用再搞一遍拓撲排序,因為 \(tarjan\) 求強連通分量每個點所在的強連通分量編號可以代替拓撲序。
如果要求字典序最小呢?用dfs列舉點 \(1 \to 2n\) ,如果這個點可以選,那就將與其相連的點都選上。如果發生衝突,就取消選擇這個點以及與其相連的點。
模板題,可以結合**感性理解。
**:
#include #include #include #define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int n=2e6+7;
vectoredge[n];
stacksta;
int dfn[n],low[n];
int leader[n]; // 所屬強連通分量編號
int n,m,deep,cnt;
inline void addedge(int u,int v)
inline void tarjan(int u)
2 SAT學習筆記
由對稱性解2 sat問題 2 sat解法 上面兩篇 很清楚的介紹了什麼是2 sat以及一些原理演算法 2 sat問題是圖論中乙個比較有意思的問題,重點是建圖,對於邊的意思,就是如果你選了i,就必須選j。2 sat問題有個很明顯的地方就是對於每個i,i包含兩個點,i表示選第乙個點,i 表示選第二個點,...
2 SAT學習筆記
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一定不同 一般思路是對於乙個變數,建立兩個變數 正,反 ...
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完之後沒有判為無解,那麼以...