SAT 是 NP-complete,但 2-SAT 是 P——只差一個「每個子句最多兩個變數」的限制,難度天壤之別。

問題形式

n 個布林變數 x₁, x₂, …, xₙ,m 個子句,每個子句是「兩個文字的 OR」:

(x₁ ∨ ¬x₂) ∧ (¬x₁ ∨ x₃) ∧ (x₂ ∨ x₃)

文字 = 變數 xᵢ 或其否定 ¬xᵢ

問:是否存在賦值使所有子句同時為真?若存在,給出一組解。

轉化為蘊含圖

每個子句 (a ∨ b) 等價於兩條蘊含:

(a ∨ b) = (¬a → b) ∧ (¬b → a)
  若 a 為假,b 必須為真
  若 b 為假,a 必須為真

建圖:
  節點 2i = xᵢ(正文字)
  節點 2i+1 = ¬xᵢ(負文字)

每個子句 → 兩條有向邊
(x₁ ∨ ¬x₂):
  ¬x₁ → ¬x₂(若 x₁ 假則 x₂ 必假)
  x₂  → x₁ (若 x₂ 真則 x₁ 必真)

用 Tarjan SCC 求解

boolean solve() {
    tarjanSCC(graph);
 
    for (int i = 0; i < n; i++) {
        // xᵢ 和 ¬xᵢ 在同一個 SCC → 邏輯矛盾,無解
        if (sccId[2*i] == sccId[2*i + 1])
            return false;
    }
 
    // 賦值:Tarjan 按逆拓撲序編號
    // sccId[xᵢ] > sccId[¬xᵢ] → xᵢ = true
    for (int i = 0; i < n; i++)
        assignment[i] = sccId[2*i] > sccId[2*i + 1];
 
    return true;
}

為什麼 SCC 能解決這個問題?

蘊含圖的 SCC 內所有節點「邏輯上等價」——若某條路徑能從 xᵢ 推出 ¬xᵢ,且也能從 ¬xᵢ 推出 xᵢ,那麼兩者在同一個 SCC 裡,就是矛盾無解。

賦值時,在拓撲序後面的 SCC(Tarjan 編號更大的)代表「更多蘊含的結果」——把這一側設為 true 更容易同時滿足所有蘊含。

競程應用

2-SAT 的典型轉化:

「xᵢ 和 xⱼ 不能同時為真」= (¬xᵢ ∨ ¬xⱼ)
→ 一個子句

「xᵢ 必須為真」= (xᵢ ∨ xᵢ)
→ 只新增 ¬xᵢ → xᵢ 一條蘊含邊

排班問題(每個人值班或休假,某些組合不行)、電路設計約束、圖著色的特殊情況,都可以建模成 2-SAT。


2-SAT 的優雅在於:把「能否同時滿足」的問題轉化成「圖上是否有矛盾路徑」——邏輯問題變成圖論問題。