Why Exact Cover Matters
Suppose a puzzle board must be tiled with a fixed collection of pieces. Every board cell must be occupied once, and every piece must be used once. A proposed placement satisfies several requirements together: it uses one piece and occupies several cells. Choosing placements independently can create overlaps or leave gaps. Exact cover makes both mistakes visible in one model: give each requirement a column, give each legal placement a row, and select rows that put exactly one selected entry in every column.
The same representation works for Sudoku candidates, small assignment puzzles, and other systems of discrete choices with exact-once requirements. It extends the choose, recurse, undo pattern from recursion and constraint search: a choice removes the requirements it satisfies and excludes every incompatible choice. A remaining requirement with no candidate proves that the current branch cannot work.
Algorithm X is the search procedure. Dancing Links, usually abbreviated DLX, is a sparse, reversible representation that makes the procedure practical. This lesson develops the exact-once model, traces an unsuccessful branch followed by a successful one, implements indexed DLX in three languages, and explains both its restoration invariant and its exponential worst case.
Exact cover selects pairwise disjoint candidate subsets whose union is the entire required universe. Algorithm X branches on the candidate rows for one uncovered requirement; Dancing Links makes each branch reversible by detaching conflicting entries and restoring their saved links during backtracking.
Core Concepts and Terminology
Let the universe be $U=\{0,1,\ldots,m-1\}$ and let the input have $n$ labeled, nonempty subsets $R_0,\ldots,R_{n-1}\subseteq U$. A solution is a set of row IDs $I$ such that
$$\bigcup_{i\in I}R_i=U,\qquad R_i\cap R_j=\varnothing\quad\text{for distinct }i,j\in I.$$
Equivalently, with incidence matrix $A\in\{0,1\}^{n\times m}$, choose binary variables $x_i$ satisfying
$$\sum_{i=0}^{n-1}A_{i,c}x_i=1\qquad\text{for every }c\in U.$$
The right-hand side is exactly one. Ordinary set cover permits overlapping selected sets; set packing permits requirements to remain uncovered. Neither is the same problem. Exact cover asks for a feasible selection, without automatically minimizing its size or assigning it a cost.
The programs count all solutions and retain the first solution in deterministic search order. Different row IDs remain different choices even if their subsets are identical. Empty candidate rows are rejected, so the algorithm never silently omits optional empty-row selections. An empty universe with no rows has exactly one solution, the empty selection. A nonempty universe with no rows has none.
A node in the search tree is a recursive call with a particular partial selection. At that boundary, the active columns are exactly the requirements not yet satisfied; their active candidate rows are precisely those compatible with the selection. Choose the column with the fewest active candidates, breaking ties by increasing column ID. Visit its candidate rows in increasing input row ID.
Here is the concrete instance used throughout. Its six rows contain $z=17$ total incidences:
- Row 0: $\{0,3,6\}$.
- Row 1: $\{0,3\}$.
- Row 2: $\{3,4,6\}$.
- Row 3: $\{2,4,5\}$.
- Row 4: $\{1,2,5,6\}$.
- Row 5: $\{1,6\}$.
| Term | Meaning | Example and role |
|---|---|---|
| Requirement / column | An element that must be covered exactly once | Column 4 can initially be satisfied by row 2 or row 3 |
| Candidate / row | A labeled subset of requirements satisfied together | Selecting row 1 satisfies columns 0 and 3 |
| Incidence node | A stored matrix entry equal to one | Row 0 has three nodes, one per member of its subset |
| Header and root | A column sentinel; a master sentinel for active headers | Root index 0 links the currently uncovered columns |
L, R, U, D | Integer indices of circular-list neighbors | Row traversal uses L/R; column traversal uses U/D |
C, row, S | Owning header, input row ID, and current column size | S[c] counts nodes currently linked vertically to header c |
| Cover | Hide a header and detach its rows from other columns | Covering input column 0 removes three incidences elsewhere |
| Uncover | Reverse a cover using saved neighbors | Restore rows upward and then restore the header |
| Smallest-column heuristic | Branch on the most constrained uncovered requirement | After row 1, column 4 has only row 3 |
| Dead end / success | An active column of size zero / no active columns | Row 0 leads to a dead end; rows 1, 3, 5 form a cover |
How Exact Cover Works, Step by Step
At each recursive entry, increment the explored-node counter. If the root has no active column header, record the selected row IDs as one complete solution and return. This is a test for no remaining columns, not for no remaining candidate rows. Otherwise scan the active headers for a minimum-size column $c$. If $S[c]=0$, return without modifying the structure: the corresponding requirement cannot be satisfied in this branch.
For a nonempty chosen column, execute these operations in order:
- Cover $c$ once for the entire candidate loop. Detach its header from the root ring. For each node $i$ down column $c$, traverse the other nodes $j$ in its row rightward and detach each $j$ from its vertical column list. Decrement the appropriate size. The column-$c$ list itself remains available for iterating its candidates.
- Select a candidate row. Append its original row ID to the explicit selection stack. Traverse its other nodes rightward, covering each associated column. This removes the remaining requirements satisfied by that row and excludes rows that would overlap it.
- Recurse on the reduced instance. At this boundary the active matrix represents exactly the remaining compatible choices. A solution increments the total but does not end enumeration.
- Undo this row before advancing. Traverse the selected row leftward and uncover its other columns. Pop its row ID. The reduced state for the next candidate must be exactly the same as before this candidate.
- After all candidates, uncover $c$. This restores the caller's complete state. Even a successful branch is fully unwound.
The critical local operation uses the detached node's unchanged neighbor indices. Removing a vertical node $j$ assigns D[U[j]] = D[j] and U[D[j]] = U[j]. Restoring it assigns D[U[j]] = j and U[D[j]] = j. Its own U[j] and D[j] were never cleared. Uncover scans column rows upward, and visits their row nodes leftward, before relinking the header.
Covering the branch column is bookkeeping, not a selected-row decision: it removes all its candidate rows from the other lists before the program chooses which one to take. That is why you must not cover the branch column again when traversing the selected row. Each row traversal starts at the node after the node already in that column and stops upon returning to it.
The diagram labels columns using input IDs. In code, root is index 0, input column $c$ has header index $c+1$, and incidence nodes start at index $m+1$. These are integer indices into arrays, with no raw pointers and no node allocation during search. Header order and row rings remain stable under undo, making tie breaking repeatable.
flowchart TD
A["Enter search; increment nodes"] --> B{"No active columns?"}
B -->|Yes| C["Count solution; save first; return"]
B -->|No| D["Choose smallest active column c"]
D --> E{"Size of c is zero?"}
E -->|Yes| F["Dead end; return"]
E -->|No| G["Cover c once"]
G --> H{"Another candidate row in c?"}
H -->|Yes| I["Push row; cover its other columns rightward"]
I --> J["Recurse on remaining requirements"]
J --> K["Uncover other columns leftward; pop row"]
K --> H
H -->|No| L["Uncover c; return"]
Worked Example with a Full Trace
Use the six-row instance above. The initial candidate lists, in input-column order, are 0:[0,1], 1:[4,5], 2:[3,4], 3:[0,1,2], 4:[2,3], 5:[3,4], 6:[0,2,4,5]. Their sizes are $[2,2,2,3,2,2,4]$. Several columns tie at size two; choose column 0 because it comes first.
Covering column 0 hides its header and unlinks row 0's entries in columns 3 and 6, then row 1's entry in column 3. There have now been one cover and three incidence unlinks. The branch loop still has row IDs 0 and 1 available in column 0.
First candidate, row 0. Push 0, cover column 3, then cover column 6. Cover 3 removes row 2's remaining entries in columns 4 and 6. Cover 6 then removes row 4's entries in columns 1, 2, 5 and row 5's entry in column 1. The next recursive entry has active columns 1, 2, 4, 5 with sizes $[0,1,1,1]$. Column 1 has no candidate, so prune this branch. Uncover 6, uncover 3, and pop row 0. Keep column 0 covered while moving to its next candidate.
Second candidate, row 1. Push 1 and cover its other column, 3. The recursive entry now has columns 1, 2, 4, 5, 6, with sizes $[2,2,1,2,2]$. Column 4 uniquely has minimum size; its only candidate is row 3.
Forced row 3. Cover 4, push row 3, and follow its row ring from column 4: cover 5, then wrap around and cover 2. The active columns are now 1 and 6, both with row 5 as their only candidate. Choose column 1 by the tie rule.
Forced row 5. Cover 1, push row 5, and cover 6. No active columns remain. Record $[1,3,5]$: the subsets $\{0,3\}$, $\{2,4,5\}$, and $\{1,6\}$ are pairwise disjoint and cover $U$.
Every cover call, including calls with no incidence unlink, is listed below. Counters are cumulative, so uncovering never subtracts from them. The chosen column is covered before its row is pushed.
| Cover number | Input column | Selection stack at that moment | New incidence unlinks | Cumulative unlinks |
|---|---|---|---|---|
| 1 | 0 | [] | 3 | 3 |
| 2 | 3 | [0] | 2 | 5 |
| 3 | 6 | [0] | 4 | 9 |
| 4 | 3 | [1] | 2 | 11 |
| 5 | 4 | [1] | 2 | 13 |
| 6 | 5 | [1,3] | 3 | 16 |
| 7 | 2 | [1,3] | 0 | 16 |
| 8 | 1 | [1,3] | 1 | 17 |
| 9 | 6 | [1,3,5] | 0 | 17 |
After recording the cover, unwind in this exact order: uncover 6; pop 5; uncover 1; uncover 2 then 5; pop 3; uncover 4; uncover 3; pop 1; uncover 0. The failed branch had already uncovered 6 then 3 and popped 0. No sibling candidates remain in the successful branch. The complete run uses five search entries, nine cover calls, and seventeen incidence unlinks, with one solution. Every structural array and every column size matches its initial value afterward.
Trace It by Hand
Pause at the recursive entry with selection [1]. The active lists are 1:[4,5], 2:[3,4], 4:[3], 5:[3,4], 6:[4,5]. Predict the selected column, the selected row, the next two columns covered after that row is pushed, and the next active lists.
Answer: choose column 4, whose only candidate is row 3. Cover 4 before pushing 3, then follow row 3's circular order to cover 5 and 2. The remaining lists are 1:[5] and 6:[5]. The next recursive call is node 4, with seven covers and sixteen cumulative unlinks; selecting row 5 completes the cover.
Now delete row 5 from the original input. Prediction: there is no solution. Row 0 still leaves column 1 empty; the row-1 branch still forces row 3, after which column 1 has no candidate. This changes satisfiability, rather than merely changing which cover appears first.
Implementation in Python, C++, and Java
Each complete program implements the same indexed DLX search. Inputs use zero-based column IDs and input row IDs. A row may list its columns in any order: construction copies and sorts each row, preserving the caller's data. Arrays and rows must be non-null, and IDs must be integers. Validation rejects negative or out-of-range columns, a repeated column within a row, an empty row, or an input exceeding the demonstration limits.
The limits are twenty columns and twenty candidate rows. They keep recursion shallow and make all solution and instrumentation counters safe in C++ and Java's signed 64-bit types. The algorithm generalizes to larger instances, but raising those limits requires revisiting capacity, counter overflow, and runtime. The later Sudoku encoding discussion describes the model; a full Sudoku matrix exceeds these demonstration limits.
nodes counts recursive entries, including successful leaves and dead ends. covers counts cover calls, including ones with no incidence deletion. unlinks counts only vertical incidence-node removals; it excludes header removal and subsequent restoration. These totals describe implementation work and are not interchangeable with the number of selected rows.
solve() snapshots the structural arrays, resets the result and counters, enumerates all covers, and verifies exact restoration and an empty selection stack. It can be called again on the same constructed object. Only the first solution's row IDs are retained; enumeration does not store every solution. first=[] occurs both for an unsatisfiable instance and for the valid empty cover, so consult the solution count. A copied result survives the next solve.
Expected stdout, identical in all three languages:
classic: solutions=1, nodes=5, covers=9, unlinks=17first=[1,3,5], restored=YESmultiple: solutions=5, nodes=10, covers=10, unlinks=8first=[0,1,2], restored=YESmissing: solutions=0, nodes=1, covers=0, unlinks=0first=[], restored=YESduplicate rows: solutions=2, nodes=3, covers=1, unlinks=0first=[0], restored=YESempty: solutions=1, nodes=1, covers=0, unlinks=0first=[], restored=YESchecks = PASS
For multiple, the five covers are $\{0,1,2\}$, $\{0,4\}$, $\{3,2\}$, $\{5,1\}$, and $\{6\}$ as sets of row IDs. For duplicate rows, either of the two labeled rows alone covers column 0, hence two solutions. Selecting both is invalid.
The implementations were compared on 9,559 cases per language: exhaustive ordered row sequences of lengths zero through four over all seven nonempty subsets of a three-element universe; every family of at most five distinct nonempty subsets of a four-element universe; 1,800 seeded random instances; boundary fixtures; and eight invalid inputs. Of the valid cases, 6,527 were satisfiable and 3,024 were not. An independent include/exclude subset oracle verified counts, and an immutable Algorithm X model verified the first solution and all counters. Column order was shuffled within input rows, including 20,446 unsorted rows; normalization preserved results and caller data. Repeated solves produced identical results without changing inputs. Instrumented Python also checked every current column-list size and neighbor relation, and 86,836 individual cover/uncover pairs restored their exact saved arrays. C++ ran with address and undefined-behavior sanitizers; Java ran with assertions enabled.
Complexity, Correctness, and Invariants
Let $m$ be the number of columns, $n$ the number of nonempty candidate rows, $z=\sum_i\lvert R_i\rvert$ the incidence count, $h\le\min(m,n)$ the maximum selection depth, and $N$ the number of recursive entries. Discuss these parameters without the programs' fixed demonstration limits; otherwise every accepted input would have a constant-size bound.
Construction takes $O(m+n+z+\sum_i\lvert R_i\rvert\log(\max(2,\lvert R_i\rvert)))$ time because the code validates and sorts each row. For already validated, sorted rows, building the links is $O(m+n+z)$. Snapshot creation and final comparison take $O(m+z)$ time and space. The input model has no empty rows, so $n\le z$ whenever there are rows.
At a search entry, choosing the minimum column scans at most $m$ headers. A single pointer unlink is constant time, but covering a column is not: it visits all affected row nodes. Along one branch transition, covering the chosen row's columns removes at most $z$ incidence nodes and visits at most $O(m+z)$ list elements. Charge that transition to its child entry, and charge the initial branch-column cover to its parent. Reverse restoration has the same order of work. Thus a conservative whole-search bound is
$$T_{\text{search}}=O\bigl(N(m+z+1)\bigr).$$
No two paths can reach the same compatible set of chosen row IDs: their first differing choices would be two rows covering the same branch column, which cannot both belong to that set. Therefore $N\le 2^n$, giving the conservative worst-case bound $O(2^n(m+z+1))$, plus construction. Some families have exponentially many solutions, so enumerating all of them necessarily takes exponential time. Minimum-column branching is a useful heuristic, not a polynomial-time guarantee.
The best terminal search is $O(1)$ for the empty universe. A nonempty instance with a zero-size column at its root returns after an $O(m)$ header scan; construction still processes the input. There is no distribution-independent average-case bound: sparsity, row overlap, and heuristic choices determine the search tree. A sequence of forced choices also incurs repeated header scans, even though it has no branching.
All DLX arrays, the verification snapshot, the selection stack, and the saved first solution together use $O(m+n+z+h)$ space, which simplifies to $O(m+z)$ under this nonempty-row model. The algorithm stores the search path, not the whole search tree or all solutions. Returning every cover would add space proportional to the total output size.
| Approach | Conservative time, after input normalization | Auxiliary space including search state | When the assumptions fit |
|---|---|---|---|
| Indexed DLX with minimum column | $O(N(m+z+1))$; $N\le 2^n$ | $O(m+n+z+h)$ | Sparse exact-once constraints; incremental reversible updates |
| Algorithm X copying a dense reduced matrix | $O(Nmn)$ for positive $m,n$ | $O(hmn)$ with one copy per level | Small matrices where clarity matters more than copying cost; its own $N$ depends on branching |
| Include/exclude subset DFS with coverage counts | $O(2^n(m+z+1))$ as a simple bound | $O(m+n+z)$ | Small $n$; useful as an independent correctness oracle |
| Column-mask subset DP, processing rows one at a time | $O(n2^m)$ transitions | $O(2^m)$ counters plus input | Small $m$; masks fit the chosen bit representation and arithmetic cost is accounted for |
| Binary integer model with one equality per column | Solver- and formulation-dependent; no general polynomial guarantee | Solver-dependent | Extra cost, capacity, or logical conditions are central to the application |
For the mask DP, begin with one way to cover mask zero. Process each labeled row once, either skipping it or adding it only to masks disjoint from its own mask. Accumulate counts for the union masks; the full-universe mask is the answer. This is different from a recurrence that freely appends rows in arbitrary order, which would count permutations of the same cover. Large counts require adequate integer precision in both approaches.
Correctness follows by induction on the recursive calls. At entry, selected rows are pairwise disjoint, and active columns are precisely those outside their union. For any selected active column $c$, every valid completion must contain exactly one of its current candidate rows. The candidate loop explores all such possibilities; choosing one excludes exactly its overlapping rows and removes exactly its satisfied requirements. A zero-size active column rules out every completion. An empty header ring certifies that all requirements have been covered once. The branches are disjoint because distinct candidates for $c$ cannot coexist in a cover. Exact undo restores the caller's state, so an earlier branch cannot corrupt or suppress a later one.
At every recursive entry, the active header ring and its vertical lists encode exactly the requirements and candidate rows compatible with the current pairwise disjoint selection. Each successful leaf covers the entire universe once, each possible completion belongs to exactly one candidate branch, and reversing every cover reconstructs the caller's arrays and sizes exactly.
Variants, Pitfalls, and Optimizations
First solution or bounded enumeration. Returning after one cover can save substantial time, but an early-exit signal must propagate only after each active frame uncovers its selected row's other columns, pops the row, and uncovers the branch column. A search stopped after two solutions proves non-uniqueness; a count of one at that stopping point proves uniqueness only if enumeration actually completed. The supplied programs enumerate fully and have no early-exit mode.
Primary and secondary columns. Generalized exact cover distinguishes requirements that must be covered exactly once from optional constraints that may be covered at most once. Only primary headers belong in the root's branching ring; secondary columns still remove conflicts when selected. This models queens with board rows and board columns as primary constraints and diagonals as secondary constraints. Requiring every diagonal to be covered would encode a different, usually impossible problem. The supplied code treats every column as primary; it does not implement this extension.
Modeling and symmetry. For a tiling, use a column for every board cell and another for every labeled piece; otherwise the search may reuse a piece or omit one. Identical interchangeable pieces and board rotations can produce equivalent geometric answers represented by different row-ID covers. Define the intended notion of distinctness before adding symmetry breaking. Any symmetry restriction needs an argument that it retains a representative of every solution class.
Heuristics and instrumentation. Minimum column size implements the most-constrained requirement rule, using sizes maintained during updates. Ties and candidate ordering affect the first solution and the tree shape. Trying likely rows first can help when stopping at one solution; it does not by itself eliminate solutions when counting all. Degree heuristics, symmetry constraints, and preselected givens may reduce the tree, while array layout and avoiding allocation reduce per-node cost. Measure nodes as well as pointer work: two searches can explore similar trees with different update costs.
The common implementation failure is restoration in the same order as deletion. A detached node remembers the neighbors it had at deletion time, and those neighbors may themselves have been detached later. Reinstall the later changes first.
For live nodes, neighbor reciprocity must hold and the recorded size must equal the number reachable from the header. Detached nodes deliberately retain stale neighbors, so assertions that demand reciprocity from every allocated node during search are incorrect. Compare complete arrays only across a matched cover/uncover boundary or before and after the whole solve. Never clear a detached node's neighbor fields as a cleanup step.
Do not confuse a zero-size column with success, or read S as a permanent input degree. Covering updates sizes even for headers that may currently be hidden; reversing the updates matters when those headers return. Do not cover the branch column twice, advance a candidate iterator before undo, or return a reference to the mutable selection stack as the saved answer.
Preserve detached neighbors and reverse the update order: restore column rows upward, restore a selected row's columns leftward, and relink a header after its incidences. Check success by the absence of required headers, handle a remaining size-zero column as failure, and complete the undo even when adding an early-stop policy.
Practice Problems and Real-World Use
Start with a tiny universe and enumerate all row subsets as an oracle before optimizing. Check that every returned selection has coverage count exactly one in every column, then compare full solution counts. Include missing requirements, repeated labeled rows, unsorted row entries, invalid repeated columns within one row, the empty universe, and an instance whose first branch fails. Reuse the same solver object to catch incomplete restoration.
A standard $9\times9$ Sudoku encoding has $729$ possible candidate rows, one for each cell-digit choice, and $324$ primary columns: $81$ cell requirements, $81$ row-digit requirements, $81$ column-digit requirements, and $81$ box-digit requirements. Every candidate row has four incidences. Filter incompatible candidates for clues or preselect their rows using a carefully reversible operation. An individual candidate is a row of the incidence matrix, not an entire Sudoku row. These dimensions describe the unrestricted encoding before clues and exceed the example programs' size limits.
For applications with preferences or varying costs, exact-cover feasibility may be only part of the model. Consider binary integer optimization when the objective and side constraints dominate; consider a general constraint solver when propagation across richer variable domains matters. DLX is particularly natural when a sparse set of exact-once requirements already expresses the problem cleanly.
| Practice task or application | Encoding or concept tested | Difficulty |
|---|---|---|
Two requirements with rows {0}, {1}, {0,1} | Find both labeled covers and verify restoration after success | Beginner |
| Classic six-row, seven-column example | Trace the failed row-0 branch, forced choices, and nine cover calls | Intermediate |
| Tiny matrices checked against all row subsets | Verify counts, empty-universe semantics, duplicate labels, and undo | Intermediate |
| Domino tiling of a small board | One column per cell; one row per legal domino placement; enumerate covers | Intermediate |
| Tiling with a fixed labeled piece inventory | Add piece-identity columns so every piece is used once | Advanced |
| Sudoku candidate selection | Four exact-once requirements per cell-digit choice; apply givens consistently | Advanced |
| N-queens through generalized exact cover | Primary board rows/columns and secondary diagonals; requires extending the code | Advanced |
| Small team selection with exactly-once skills | A row supplies several required skills; recognize when overlapping or optional skills need another model | Intermediate |
For the original presentation, see Donald Knuth's Dancing Links, which develops Algorithm X, reversible linked updates, minimum-column branching, and generalized exact cover. The indexed programs and the detailed counter trace here are a separate teaching implementation. The distinction to retain is the one between search choices and state representation: Algorithm X organizes the alternatives, while DLX makes changing and restoring the sparse state efficient.