Given a boolean formula in conjunctive normal form, is there an assignment making it true?
The complexity cliff
| Clause size | Complexity |
|---|---|
| 1-SAT (unit clauses) | trivial |
| 2-SAT | P — via SCC |
| 3-SAT | NP-complete |
| -SAT, | NP-complete |
| Horn-SAT (≤1 positive literal per clause) | P — unit propagation |
| XOR-SAT | P — Gaussian elimination over $\mathbb{F}_2$ |
| MAX-2-SAT (maximise satisfied clauses) | NP-hard |
The jump from 2 to 3 is the sharpest boundary in complexity theory, and Horn-SAT and XOR-SAT are the other two tractable fragments worth recognising.
2-SAT — the algorithm
A clause is two implications: and . Build the implication graph on literal vertices, then:
Satisfiable iff no variable has and in the same SCC.
Assignment: set true iff is later in topological order than .
. See 2-SAT for the code and encoding recipes.
Encoding constraints
| Constraint | Clauses |
|---|---|
| true | |
| At most one of | |
| At most one of | pairwise (), or prefix-OR encoding () |
| Exactly one of | at-most-one + — the last needs , so not 2-SAT |
The at-most-one prefix encoding: introduce = “some with is true”, with clauses , , . clauses instead of .
The binary-search pattern
2-SAT decides feasibility, so pair it with a binary search to optimise:
“Place objects, each in one of two positions, maximising the minimum separation.”
Binary search the separation ; for each , add a clause for every pair of placements closer than ; test satisfiability. .
This combination is one of the most recognisable 2-SAT problem shapes in contests.
General SAT solving
Modern CDCL (conflict-driven clause learning) solvers routinely handle industrial instances with millions of variables, despite NP-completeness. The core ingredients:
| Technique | Idea |
|---|---|
| Unit propagation | a clause with one unassigned literal forces it |
| Clause learning | on conflict, derive and add a new clause explaining it |
| Non-chronological backjumping | jump back past irrelevant decisions |
| VSIDS branching | prefer variables appearing in recent conflicts |
| Watched literals | propagate without scanning every clause |
| Restarts | escape bad search regions, keeping learned clauses |
Not a contest tool, but the reason “NP-complete” does not mean “hopeless in practice”.
DPLL — the simple version
bool dpll(Formula& f) {
unitPropagate(f); // forced assignments
pureLiteralElimination(f); // literals of one polarity
if (f.empty()) return true; // all clauses satisfied
if (f.hasEmptyClause()) return false; // conflict
int v = chooseVariable(f);
return dpll(f.assign(v, true)) || dpll(f.assign(v, false));
}Unit propagation does most of the work: a clause with a single unassigned literal forces its value, which may cascade. Implementing just DPLL with unit propagation solves surprisingly large instances.
What to use when
| Problem | Method |
|---|---|
| Two choices per object, pairwise conflicts | 2-SAT |
| Optimising with a monotone threshold | binary search + 2-SAT |
| Implication chains only | Horn-SAT / unit propagation |
| XOR constraints | $\mathbb{F}_2$ Gaussian elimination |
| Small , general constraints | backtracking with propagation |
| Exact cover structure | Dancing Links |
| Maximise satisfied clauses | SDP or local search |
| Genuinely need general SAT | there is probably a better formulation |
If a contest problem seems to need general SAT, look again — the intended solution is almost always 2-SAT, flow, matching, or a DP.
See also: 2-SAT · NP-Completeness · Backtracking