圖論專題-學習筆記:2 - SAT 問題
@
目錄一些 update
update 2021/11/12:修正了文章中的一些語言。
1. 前言
本篇文章將會對 2 - SAT 問題做一個講解。
前置知識:強連通分量/縮點(我的部落格連結/強連通分量 - OI Wiki)。
2. 詳解
首先我們需要知道 2 - SAT 是什麼。
實際上 2 - SAT 是 k - SAT 的一種特殊情況。
k - SAT 問題的一般描述就是有一些物品,每個物品均有 \(k\) 個狀態,給出一些限制條件,諸如 A 為 a 狀態則 B 為 b 狀態之類的,問有無可行解,有時還會讓你求解。
目前 k - SAT 問題(\(k > 2\)
2 - SAT 一般描述如下:
現給出 \(n\) 個布林型別的值,可真可假,有 \(m\) 種限制條件,限制條件是如下 3 種情況之一:
- 若 \(A = p\),則 \(B = q\)。
- \(A = p\) 與 \(B = q\) 至少有一個成立。
- A 必須為 \(p\)。
剩餘別的所有情況都可以轉化為上述三種情況。
現給出這些限制條件,問你有沒有一組取值,使得這組取值能夠滿足所有限制條件,如果有還需要求出一組可行解。
解決 2 - SAT 問題的方案一般是建圖。
對於每一個變數 \(x\) 我們建兩個點 \(x_0,x_1\),分別表示變數 \(x\) 取 0 或者取 1。
- 若 \(A = p\),則 \(B = q\)。
為了方便,只討論若 \(A = 1\),則 \(B = 1\),其餘情況可以類推。
考慮連邊 \(A_1 \to B_1,B_0 \to A_0\),這麼連邊的意義是如果 \(A = 1\),那麼根據連邊可以推出 \(B = 1\),如果 \(B = 0\),那麼根據連邊可以推出 \(A = 0\)。
注意 \(B_0 \to A_0\) 正確性是因為原命題的逆否命題正確。
-
\(A = p\)
為了方便,只討論 \(A = 1\) 與 \(B = 1\) 至少有一個成立,其餘情況可以類推。
考慮連邊 \(A_0 \to B_1,B_0 \to A_1\),意義是如果 \(A = 0\) 則 \(B = 1\) 必然成立,同理 \(B = 0\) 則 \(A = 1\) 必然成立。
- A 必須為 \(p\)。
為了方便,只討論 \(A = 1\) 的情況,其餘情況可以類推。
對於這種情況,直接連邊 \(A_0 \to A_1\),意義是如果 \(A = 0\),根據連邊可以發現 \(A = 1\),這意味著 \(A \ne 0\),只能是 \(A = 1\),該連邊可以控制 \(A \ne 0\)。
這麼操作之後,我們得到了一張有向圖,其中的有向邊表示推出關係。
那麼接下來如何判斷有無解呢?
採用強連通分量演算法。
假設說某變數 \(x\),其兩個點 \(x_0,x_1\) 處於同一個強連通分量中,這說明從 \(x = 0\) 出發能夠推出 \(x = 1\),從 \(x = 1\) 出發能夠推出 \(x = 0\),因此 \(x \ne 0,x \ne 1\),這樣就無解了。
那麼如何求出一組可行解呢?
首先對這一張圖縮點,然後我們就得到了一張 DAG。
如果 \(x_0\) 處於強連通分量 \(A\) 中,\(x_1\) 處於強連通分量 \(B\) 中,且 \(A\) 的拓撲序小於 \(B\),那麼有兩種可能:
- \(x_0,x_1\) 互不干擾。
- \(x_0\) 能夠推出 \(x_1\)。
發現無論哪種情況選擇 \(x_1\) 都是正確的,因此對於變數 \(x\) 而言,\(x_0,x_1\) 哪個拓撲序大選哪個能夠保證選出來的解是正確的。
所以我們需要對建出來的圖縮點,然後跑拓撲排序,求出拓撲序,然後求解……
嗎?
不需要!在求強連通分量的時候我們已經得到拓撲序了。
若採用 Kosaraju 演算法(兩遍 dfs):
求強連通分量的時候我們會得到一個 Color[]
陣列表示其屬於的強連通分量的編號,而根據 Color[]
陣列可以直接推出兩個強連通分量的拓撲序大小:
- 對於縮點之後的圖上的兩個點 \(i,j\),如果 \(Color_i < Color_j\),則 \(i\) 一定能夠走到 \(j\)。換言之,求出這張圖的拓撲序後 \(i\) 在 \(j\) 前面。
上述結論的證明過程我在我的文章圖論專題-學習筆記:強連通分量中證明過,此處不再證明,想了解的讀者可以進入我的文章瞭解。
實質上 Color[]
就是新圖的拓撲序(之一)。
若採用 Tarjan 演算法:
採用 Tarjan 演算法需要注意的一點就是求出的 scc[]
陣列是反過來的,也就是說 scc[]
越大其拓撲序越小,這點跟 Kosaraju 演算法是不一樣的,也比較容易出錯。
證明的話實際上是因為 Tarjan 求強連通分量的時候我們是倒著標記強連通分量的,因此拓撲序最小的強連通分量其標號是最大的。
我更推薦 Kosaraju 演算法~
洛谷模板題:P4782 【模板】2-SAT 問題。
洛谷的模板題只有情況 2。
Code:GitHub CodeBase-of-Plozia P4782 【模板】2-SAT 問題.cpp
3. 總結
2 - SAT 問題的一般解法是根據邏輯關係進行拆點連邊,然後求強連通分量判斷有無解以及求出一組解。