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 |
| 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.
Proof: Horn-SAT and XOR-SAT are polynomial
Horn-SAT. Every clause has at most one positive literal, so each clause is either a pure negative clause (, i.e. “not all of these”) or an implication (rewriting the single positive literal and negated literals as a conjunction of premises). Unit propagation — repeatedly finding a clause whose premises are all already satisfied and forcing its conclusion — can only set variables to true (never needs to guess), so it runs to a unique fixed point in (or with a counter-based queue); the formula is satisfiable iff this fixed point violates no purely negative clause, since the fixed point is the minimal model (setting as few variables true as forced) and if any model exists, the minimal one — a subset of any other satisfying assignment’s true variables — also satisfies every clause (a Horn clause with extra false premises or an already-true conclusion stays satisfied when fewer variables are set true).
XOR-SAT. Each clause (or derived from / structure restricted to parity constraints) is a linear equation over . The whole formula is a linear system, solvable (or shown inconsistent) by Gaussian elimination in with bitset rows — satisfiability is exactly solvability of the linear system, a standard fact of linear algebra over any field.
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, the full correctness proof, 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 .
Proof: the at-most-one prefix encoding
The clauses force to be true whenever any of is true: handles itself, and propagates it forward once set, so by induction “some , , is true” is forced in the "" direction by these two clause families (a satisfying assignment with some earlier true must set every for true, since the implications chain forward). The third clause then says: if some earlier variable was already true (forcing true), cannot also be true — directly preventing two variables from being simultaneously true, since the later one’s clause fires against the earlier one’s already-forced . Together these clauses (roughly) enforce “at most one of is true” using clauses and auxiliary variables, instead of the pairwise clauses needed without the prefix trick.
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.
Proof: the binary search is valid
Binary search requires the feasibility predicate (“can every pair be placed with separation ?”) to be monotone in : if and separation is achievable, then is too, using the same assignment (every pair that was at least apart is automatically at least apart). So the set of feasible is a prefix for some threshold , and binary search finds in feasibility checks, each an clause-construction plus an 2-SAT solve.
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 | 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.