Given a boolean formula in conjunctive normal form, is there an assignment making it true?

The complexity cliff

Clause sizeComplexity
1-SAT (unit clauses)trivial
2-SATP via SCC
3-SATNP-complete
-SAT, NP-complete
Horn-SAT (≤1 positive literal per clause)P — unit propagation
XOR-SATPGaussian 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

ConstraintClauses
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:

TechniqueIdea
Unit propagationa clause with one unassigned literal forces it
Clause learningon conflict, derive and add a new clause explaining it
Non-chronological backjumpingjump back past irrelevant decisions
VSIDS branchingprefer variables appearing in recent conflicts
Watched literalspropagate without scanning every clause
Restartsescape 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

ProblemMethod
Two choices per object, pairwise conflicts2-SAT
Optimising with a monotone thresholdbinary search + 2-SAT
Implication chains onlyHorn-SAT / unit propagation
XOR constraints$\mathbb{F}_2$ Gaussian elimination
Small , general constraintsbacktracking with propagation
Exact cover structureDancing Links
Maximise satisfied clausesSDP or local search
Genuinely need general SATthere 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