\[}\]
我們舉乙個簡單的例子:
機房裡有三位大佬小s,小l,小m和乙個蒟蒻tqr,他們刷題時有不同的要求(因為蒟蒻什麼題都不會做,故不列舉):
大佬/要求
小s小l
小m 要求1
不包含數論知識(¬a)
包含數論知識(a)
不包含數論知識(¬a)
要求2包含幾何知識(b)
包含幾何知識(b)
不包含幾何知識(¬b)
要求3不包含圖論知識(¬c)
不包含圖論知識(¬c)
包含圖論知識(c)
在此之前,複習一下前置數學知識:我們繼續上面的情景,如果我們要出一道題,使這道題能同時滿足上面三位dalao的喜好,該如何做?對於乙個命題:如果a,那麼b
其逆命題為:如果b,那麼a
其否命題為:如果¬a,那麼¬b
其逆否命題為:如果¬b,那麼¬a
∧表示與,∨表示或,¬表示非
原命題與逆否命題等值。逆命題與否命題等值
原命題與逆命題以及逆否命題與否命題之間沒有關係
經過數學分析,滿足的題目一定符合以下條件(¬a∨b∨¬c)∧(a∨b∨¬c)∧(¬a∨¬b∨c)
因此我們要做的就是給每個變數賦值,使上式值為true
是的,這就是sat問題,但需要注意的是,上面的情況中,每個同學對題目都有三個限制,因此是3-sat問題
可證明的,3(及以上)-sat問題都是npc問題(即不可使用演算法解決,唯一的方法是暴力列舉)
因此,2-sat問題是演算法能解決的極限,那麼我們將限制改一改:
經過一段時間的學習後,除了tqr,其他人都完全掌握了圖論知識……一波操作過後,限制條件變成了
(¬a∨b)∧(a∨b)∧(¬a∨¬b)
,這就是我們要學習的2-sat了
那我們就開始吧……
首先,我們需要將幾個互相有限制的點賦值,那就把它們丟到圖裡面!
建立兩個點,分別是a和¬a(儲存時,可以存到編號分別為 \(i\) 和 \(i+maxn\) 的節點上,就像並查集對稱點一樣)
那麼每個點之間的關係是什麼?
(a∨b)
可以理解為若a為真,則b為假,反之b為真
於是我們可以建出圖形:
由圖形可以發現,a與¬b,以及¬a與b都在同乙個強連通分量裡,於是我們可以得出結論:
2-sat問題同乙個強連通分量中的所有元素值相同很顯然的,如果a與¬a(或者x與¬x,x代表任意條件)在同乙個強連通分量裡,即他們的值相等,那麼出現了矛盾,可判斷問題無解(就像1=-1一樣,矛盾的等式)
有n個布林變數 \(x_1\)~\(x_n\),另有 \(m\) 個需要滿足的條件,每個條件的形式都是 \(x_i\)為很顯然地,我們需要按照上述所說的來建邊true/false
或 \(x_j\)為true/false
。你的mubiao給每個變數賦值使得所有條件得到滿足。第一行兩個整數\(n\)和\(m\),意義如題面所述
接下來\(m\)行每行\(4\)個整數\(i,a,j,b\),表示\(x_i\)為\(a\)或\(x_j\)為\(b\)」\((a,b∈\)\)
如無解,輸出\(impossible\);否則輸出\(possible\),
下一行\(n\)個整數\(x_1\)~\(x_n\)
\((x_i∈\)\),表示構造出的解。
read(n);read(m);
for(register int i=1;i<=m;++i)
然後tarjan找強連通分量(color陣列是拓撲序)
void tarjan(int u)
else if(vis[v]) low[u]=min(low[u],dfn[v]);
} if(dfn[u]==low[u]) }
}for(register int i=1;i<=2*n;++i) if(!dfn[i]) tarjan(i);
如果強連通分量 \(x_0\), \(x_1\) 在縮點後 \(dag\) 的底圖中連通,那麼我們選擇的一定是拓撲序較大的
\(tarjan\) 求強連通分量得到的強連通分量編號是遵循拓撲序逆序的,所以如果 \(x_0\) 的編號更小,輸出 \(0\),否則輸出 \(1\)
for(register int i=1;i<=n;++i)
if(color[i]==color[i+n])
puts("possible");
for(register int i=1;i<=n;++i)
printf(color[i]>color[i+n]?"1 ":"0 ");
這道題就很輕鬆地解決了,下面是完整**
#includeusing namespace std;
int n,m,a,va,b,vb;
struct edge
edge[2000005];
int cnt=0,head[2000005];
inline void add_edge(int from,int to)
int low[2000005],dfn[2000005],color[2000005],tms,idx;
bool vis[2000005];
stacksta;
void tarjan(int u)
else if(vis[v]) low[u]=min(low[u],dfn[v]);
} if(dfn[u]==low[u]) }
}templateinline void read(t &res)
int main()
for(register int i=1;i<=2*n;++i) if(!dfn[i]) tarjan(i);
for(register int i=1;i<=n;++i)
if(color[i]==color[i+n])
puts("possible");
for(register int i=1;i<=n;++i)
printf(color[i]>color[i+n]?"1 ":"0 ");
return 0;
}
有什麼不懂的可以發qq問我,沒了
模板 2 SAT 問題 2 SAT
2 sat 問題 模板 有n個布林變數 x 1 x n 另有m個需要滿足的條件,每個條件的形式都是 x i 為true false或 x j 為true false 比如 x 1 為真或 x 3 為假 x 7 為假或 x 2 為假 2 sat 問題的目標是給每個變數賦值使得所有條件得到滿足。輸入格式...
2 SAT學習筆記
由對稱性解2 sat問題 2 sat解法 上面兩篇 很清楚的介紹了什麼是2 sat以及一些原理演算法 2 sat問題是圖論中乙個比較有意思的問題,重點是建圖,對於邊的意思,就是如果你選了i,就必須選j。2 sat問題有個很明顯的地方就是對於每個i,i包含兩個點,i表示選第乙個點,i 表示選第二個點,...
2 SAT學習總結
2 sat 圖論演算法 理解 給定乙個布林方程,判斷是否存在一組布林變數的取值方案,使得整個方程值為真的問題,被稱為布林方程的可滿足性問題 sat sat 問題是 np 完全的,但對於一些特殊形式的 sat 問題我們可以有效求解。我們將下面這種布林方程稱為合取正規化 其中稱為文字,它是乙個布林變數或...