2-SAT¶
SAT (Boolean satisfiability problem) is the problem of assigning Boolean values to variables to satisfy a given Boolean formula.
The Boolean formula will usually be given in CNF (conjunctive normal form), which is a conjunction of multiple clauses, where each clause is a disjunction of literals (variables or negation of variables).
2-SAT (2-satisfiability) is a restriction of the SAT problem, in 2-SAT every clause has exactly two literals.
Here is an example of such a 2-SAT problem.
Find an assignment of
SAT is NP-complete, there is no known efficient solution for it.
However 2SAT can be solved efficiently in
Algorithm:¶
First we need to convert the problem to a different form, the so-called implicative normal form.
Note that the expression
We now construct a directed graph of these implications:
for each variable
Let's look at the example in 2-CNF form:
The oriented graph will contain the following vertices and edges:
You can see the implication graph in the following image:

It is worth paying attention to the property of the implication graph:
if there is an edge
Also note, that if
In order for this 2-SAT problem to have a solution, it is necessary and sufficient that for any variable
This criterion can be verified in
The following image shows all strongly connected components for the example.
As we can check easily, neither of the four components contain a vertex

Now we construct the algorithm for finding the solution of the 2-SAT problem on the assumption that the solution exists.
Note that, in spite of the fact that the solution exists, it can happen that
Let us sort the strongly connected components in topological order (i.e.
Let us prove that with this assignment of the variables we do not arrive at a contradiction.
Suppose
First we prove that the vertex
Secondly we prove that there doesn't exist a variable
So we have constructed an algorithm that finds the required values of variables under the assumption that for any variable
Implementation:¶
Now we can implement the entire algorithm.
First we construct the graph of implications and find all strongly connected components.
This can be accomplished with Kosaraju's algorithm in
Afterwards we can choose the assignment of
Below is the implementation of the solution of the 2-SAT problem for the already constructed graph of implication
struct TwoSatSolver {
int n_vars;
int n_vertices;
vector<vector<int>> adj, adj_t;
vector<bool> used;
vector<int> order, comp;
vector<bool> assignment;
TwoSatSolver(int _n_vars) : n_vars(_n_vars), n_vertices(2 * n_vars), adj(n_vertices), adj_t(n_vertices), used(n_vertices), order(), comp(n_vertices, -1), assignment(n_vars) {
order.reserve(n_vertices);
}
void dfs1(int v) {
used[v] = true;
for (int u : adj[v]) {
if (!used[u])
dfs1(u);
}
order.push_back(v);
}
void dfs2(int v, int cl) {
comp[v] = cl;
for (int u : adj_t[v]) {
if (comp[u] == -1)
dfs2(u, cl);
}
}
bool solve_2SAT() {
order.clear();
used.assign(n_vertices, false);
for (int i = 0; i < n_vertices; ++i) {
if (!used[i])
dfs1(i);
}
comp.assign(n_vertices, -1);
for (int i = 0, j = 0; i < n_vertices; ++i) {
int v = order[n_vertices - i - 1];
if (comp[v] == -1)
dfs2(v, j++);
}
assignment.assign(n_vars, false);
for (int i = 0; i < n_vertices; i += 2) {
if (comp[i] == comp[i + 1])
return false;
assignment[i / 2] = comp[i] > comp[i + 1];
}
return true;
}
void add_disjunction(int a, bool na, int b, bool nb) {
// na and nb signify whether a and b are to be negated
a = 2 * a ^ na;
b = 2 * b ^ nb;
int neg_a = a ^ 1;
int neg_b = b ^ 1;
adj[neg_a].push_back(b);
adj[neg_b].push_back(a);
adj_t[b].push_back(neg_a);
adj_t[a].push_back(neg_b);
}
static void example_usage() {
TwoSatSolver solver(3); // a, b, c
solver.add_disjunction(0, false, 1, true); // a v not b
solver.add_disjunction(0, true, 1, true); // not a v not b
solver.add_disjunction(1, false, 2, false); // b v c
solver.add_disjunction(0, false, 0, false); // a v a
assert(solver.solve_2SAT() == true);
auto expected = vector<bool>(True, False, True);
assert(solver.assignment == expected);
}
};