NeFut Logo NeFut
Admin Login

In-Depth Analysis of 2-SAT Problem: Efficient Solutions Based on Topological Sorting and Strongly Connected Components

Published at: 2026-05-27 09:17 Last updated: 2026-06-06 13:04
#C++ #Tutorial

Core Logic and Mathematical Principles

Topological sorting plays a crucial role in the algebraic construction for solving the construction problem of 2-SAT (2-Satisfiability).

The goal of the 2-SAT problem is: given $N$ Boolean variables $x_1, x_2, \dots, x_n$, and $M$ constraints, each of which is in the form "either $x_i$ is true or $x_j$ is true" ($x_i \lor x_j$). We need to assign truth values (0 or 1) to each variable such that all conditions are satisfied simultaneously.

1. Algebraic Isomorphism from Propositional Logic to Directed Graphs (Implication Graphs)

According to the equivalent transformations of logical algebra, a disjunctive condition $A \lor B$ is mathematically equivalent to two implications:

$$\neg A \implies B \quad \text{and} \quad \neg B \implies A$$

2. Legitimacy Criterion for Strongly Connected Components (SCC)

Utilizing Tarjan's algorithm or Kosaraju's algorithm to compress the strongly connected components of the implication graph:

3. Greedy Consistent Assignment Driven by Reverse Topological Order

Once a solution is determined, how do we construct a specific valid solution?


Algorithm Derivation and State Design

1. Implication Graph State Design

For variable $i \in [1, n]$:

Common constraint transformations:

  1. $x_i \lor x_j$ (at least one is true): $\neg x_i \implies x_j$ and $\neg x_j \implies x_i$. Edges: add_edge(i + n, j), add_edge(j + n, i).
  2. $x_i \implies x_j$ (if $i$ is true, then $j$ must be true): Edges: add_edge(i, j), add_edge(j + n, i + n).
  3. $x_i$ must be true: equivalent to $x_i \lor x_i$. Edge: add_edge(i + n, i).

C++ Standard Source Code (High-Precision Template for 2-SAT Construction)

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>

const int MAXN = 200005; // 2 * N space base

std::vector<int> adj[MAXN * 2];
int dfn[MAXN * 2], low[MAXN * 2], scc[MAXN * 2];
bool in_stack[MAXN * 2];
std::stack<int> stk;
int timer = 0, scc_cnt = 0;

void tarjan(int u) {
    dfn[u] = low[u] = ++timer;
    stk.push(u);
    in_stack[u] = true;

    for (size_t i = 0; i < adj[u].size(); ++i) {
        int v = adj[u][i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = std::min(low[u], low[v]);
        } else if (in_stack[v]) {
            low[u] = std::min(low[u], dfn[v]);
        }
    }

    if (low[u] == dfn[u]) {
        scc_cnt++;
        while (true) {
            int v = stk.top();
            stk.pop();
            in_stack[v] = false;
            scc[v] = scc_cnt; // Tarjan marks the scc number naturally forms reverse topological order
            if (u == v) break;
        }
    }
}

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int n, m;
    if (!(std::cin >> n >> m)) return 0;

    for (int k = 0; k < m; ++k) {
        int i, a, j, b;
        // Input format: variable i takes value a(0/1) or variable j takes value b(0/1)
        std::cin >> i >> a >> j >> b;

        // State mapping: a == 0 represents false(i+n), a == 1 represents true(i)
        int u_true = i, u_false = i + n;
        int v_true = j, v_false = j + n;

        // Determine the actual state based on 0/1 input
        int pos_u = (a == 1) ? u_true : u_false;
        int neg_u = (a == 1) ? u_false : u_true;
        int pos_v = (b == 1) ? v_true : v_false;
        int neg_v = (b == 1) ? v_false : v_true;

        // Core condition transformation: pos_u V pos_v  <=>  (neg_u -> pos_v) AND (neg_v -> pos_u)
        adj[neg_u].push_back(pos_v);
        adj[neg_v].push_back(pos_u);
    }

    // 1. Run Tarjan to partition the strongly connected topological space
    for (int i = 1; i <= 2 * n; ++i) {
        if (!dfn[i]) {
            tarjan(i);
        }
    }

    // 2. Completeness compliance check
    for (int i = 1; i <= n; ++i) {
        if (scc[i] == scc[i + n]) {
            std::cout << "IMPOSSIBLE\n"; // Positive and negative states belong to the same SCC, logic collapse
            return 0;
        }
    }

    std::cout << "POSSIBLE\n";
    // 3. Use the reverse topological property of the strongly connected component numbers to construct a greedy consistent solution
    for (int i = 1; i <= n; ++i) {
        // The smaller the scc number, the closer it is to the topological order.
        // If scc[i] < scc[i+n], it means the positive state i is behind, so choose the positive state (assign as 1)
        if (scc[i] < scc[i + n]) {
            std::cout << 1 << (i == n ? "" : " ");
        } else {
            std::cout << 0 << (i == n ? "" : " ");
        }
    }
    std::cout << "\n";

    return 0;
}

NOIP Practical Pitfall Guide

  1. Confusing Strongly Connected Component Numbers with Topological Order Directions: This is the most frequent failure point when outputting solutions for 2-SAT. The scc component numbers generated by Tarjan's algorithm are essentially in "reverse topological order" (i.e., the leaf subtree that gets cut off first has an scc number of 1). Therefore, scc[i] < scc[i+n] means that node $i$ is positioned further back in the directed graph's topological network. To avoid logical contamination, we should select the later node, hence we should choose $i$, which means the solution outputs 1. If contestants mistakenly remember that "smaller numbered topological nodes are ahead", it will invert the logic and lead to a complete loss.
  2. Not Allocating 2x (or 4x) Array Space: 2-SAT involves splitting variables, which directly doubles the total number of nodes to $2N$. Since each disjunctive constraint corresponds to two implication edges, the total number of edges also doubles to $2M$. Contestants must unwaveringly allocate 2x (for nodes) and 4x (for edges) space for the adj adjacency list base, as well as Tarjan-related dfn, low, and scc arrays; otherwise, serious out-of-bounds issues will arise during edge construction and recursive relaxation.

Classic NOIP/Luogu Real Questions

1. Luogu P4782 [Template] 2-SAT Problem

2. Luogu P3201 [HNOI2009] Dream Pudding

Original Source: local://12.4

[h] Back to Home