Exercises — Module 09 (Backtracking and Dancing Links, §7.2.2, §7.2.2.1)
Self-contained problems on this module's material — the backtrack tree and its size (Algorithm B), the all-solutions vs first-solution distinction, the cover/uncover couplet of dancing links, secondary items (the XC variant), and Knuth's random-probe tree-size estimator. You can work every one without the books: each states the problem in full, gives a hint to peek at when stuck, and a worked answer sketch to check against after you try. Computational answers here are reproduced by the code you write in the lab (or a few lines at a REPL).
Ratings follow Knuth's scale (00–50; M = needs mathematics, ▶ = especially
instructive). Where a problem mirrors a TAOCP exercise, its number is noted for
readers who own Volume 4B.
Tracking
| # | Topic | Rating | Status |
|---|---|---|---|
| 1 | Draw the queens tree and count its nodes | 10 | ⬜ |
| 2 | ▶ All-solutions vs first-solution in Algorithm B | 22 | ⬜ |
| 3 | Prove cover/uncover are exact inverses | 26 | ⬜ |
| 4 | ▶ Add secondary items to ExactCover (the XC variant) | 30 | ⬜ |
| 5 | Knuth's random-probe tree-size estimator | 35 | ⬜ |
Problems
1. Draw the queens tree and count its nodes (rating 10 · cf. 7.2.2–1)
Problem. In the row-by-row backtracker (Algorithm B), is the column of the queen in row , and a partial placement is legal when every pair of placed queens satisfies and . Count the nodes of the search tree for , where a node is a legal partial placement — the empty placement at the root, through complete solutions at the leaves. Give the count level by level.
Hint. Level is the single empty placement. Level has all four columns (no earlier queen to conflict). For each deeper level, extend each legal placement of the level above by every column that survives the property test.
Answer sketch. Counting legal partial placements by level (row just filled):
| level | legal placements | count |
|---|---|---|
| 0 | (empty) | 1 |
| 1 | 4 | |
| 2 | 6 | |
| 3 | 4 | |
| 4 | (the two solutions) | 2 |
Total nodes. For example, from only columns survive at row 1 (column 1 shares a diagonal); the two solutions are and , matching the README's count of two. (The solution counts are OEIS A000170; here we count tree nodes, not just the leaves.)
2. ▶ All-solutions vs first-solution in Algorithm B (rating 22 · cf. 7.2.2–5)
Problem. Algorithm B's step B2 says: "If , VISIT and go
to B5." The lab exposes both count_queens_solutions (find all) and
first_queens_solution (find one). Explain precisely where in B1–B5 the two
behaviours diverge, and quantify the savings for .
Hint. The two searches build the identical tree; they differ only in what happens after a complete solution is visited. One keeps backtracking; the other stops the whole search. So the first-solution search explores only the part of the tree to the left of (and including the path to) the first leaf.
Answer sketch. The trees are identical up to the moment of the first VISIT. All-solutions: after VISIT it falls through to B5 and keeps backtracking, eventually enumerating every leaf — for it walks all nodes and reports both solutions and . First-solution: the instant B2 reaches it returns instead of continuing to B5, so it stops at the leftmost complete leaf. For the leftmost solution is , reached after exploring only the subtree (which yields no solution) and the path : 9 nodes versus 17. The saving grows with (e.g. first-solution for touches 114 nodes against 2057 for the full tree). The distinction is purely control-flow — "return on VISIT" vs "record and continue" — with no change to the property test or the tree's shape.
3. Prove cover/uncover are exact inverses (rating 26 · cf. 7.2.2.1–8)
Problem. In dancing links, removing a node from its doubly linked list is
and restoring it is
cover(c) removes column header and, for every row using , removes that
row's other nodes; uncover(c) reverses this, walking rows and nodes in the
opposite order. Prove that cover(c) immediately followed by uncover(c)
restores the entire structure bit for bit.
Hint. Two ingredients. (a) A single remove/restore pair on one node is an
identity provided 's own left[x] and right[x] are untouched between them.
(b) When several nodes are removed, restoring must be in the exact reverse (LIFO)
order, because a later removal may overwrite a neighbour link that an earlier
node depends on.
Answer sketch. Single node. Remove sets its two neighbours to point past
; it does not modify left[x] or right[x]. So after removal still
records its original neighbours. Restore reads those intact links and reinstates
, , returning
both neighbours' links to their pre-removal values. If no other operation touched
left[x]/right[x] in between, the pair is the identity on all four link fields.
Many nodes — why reverse order. cover(c) removes nodes in some sequence
. Two consecutive removals in the same list interact: when
and later are neighbours, removing rewrites a link that points
around the already-removed . Restoring before would splice
into a list from which is still absent, corrupting the pointers. Restoring
in reverse order guarantees that when is restored, every
node removed after it has already been put back, so the list around is
exactly as it was the instant before left — and part (a) applies to make
each restore an identity. By induction over the operations, uncover(c)
(which visits rows bottom-to-top and nodes right-to-left, the mirror of cover)
returns every link field to its original value. Hence solving the same instance
twice yields identical results — the property Stage 3 checks, and the reason no
copy of the matrix is ever needed.
4. ▶ Add secondary items to ExactCover (the XC variant) (rating 30 · cf. 7.2.2.1)
Problem. Exact cover requires every item covered exactly once. In the XC
variant, items split into primary (must be covered exactly once) and
secondary (may be covered at most once — zero or one time). Describe the
minimal change to the dancing-links ExactCover of Stage 3 that supports
secondary items, and argue it is correct. (Concrete use: the -queens diagonals
are naturally secondary — a diagonal need not be used, but no two queens may
share one.)
Hint. Two places touch the "which items remain" logic: the MRV column-choice scan and the termination test. Secondary items must still be covered when an option uses them (so conflicts are enforced), but they must never be chosen as the branching column, and leftover uncovered secondaries must not block a solution. The classic trick: link the secondary headers among themselves but not into the root's active header list.
Answer sketch. Keep the identical node mesh and the identical cover/uncover
couplet. The only structural change is the header list: primary headers form the
circular list hanging off the root (as before), while each secondary header
is given a self-loop (its left/right point to itself) and is left out of the
root list. Consequences, all correct:
- Branching. The MRV scan walks only the root's active headers, so it only ever chooses a primary column — exactly right, since we must satisfy every primary item but are free to leave secondaries.
- Conflict enforcement. When an option that uses a secondary item is selected,
coverstill removes that secondary's column and all rows touching it, so no second option can also use it — enforcing "at most once." - Termination. The search succeeds when the root's header list is empty, i.e. all primary items are covered. Secondary items still uncovered are simply never required, which is the "at most once (possibly zero)" semantics.
Because cover/uncover are unchanged, the reversibility proof of Problem 3
still holds verbatim; only the set of headers eligible for selection shrank. This
one-field change (self-loop the secondaries, omit them from the root list) is why
the README says your ExactCover is "one field away from XC." For -queens as
exact cover: the row/column items are primary, the diagonal items are
secondary — and the same engine that solved the toy example now enforces the
diagonal constraints without demanding every diagonal be occupied.
5. Knuth's random-probe tree-size estimator (rating 35 · cf. 7.2.2)
Problem. Knuth's Monte Carlo estimator predicts the size of a backtrack tree without traversing it: take a single random walk from the root; at each node count its children , pick one child uniformly, and accumulate. Formally, if are the child-counts along the random path (with a uniform choice at each step), the estimator is
Prove that equals the exact number of nodes in the tree, and confirm it empirically against the true -queens tree sizes for .
Hint. Use linearity of expectation over nodes, not over paths. Fix a node at depth with ancestors having child-counts . What is the probability the random walk reaches ? What does add when it does? Multiply.
Answer sketch. Write , where the term contributed when the walk reaches node at depth is (the product of branching factors of 's ancestors; for the root the empty product is ). The walk reaches a specific node iff it makes the correct uniform choice at each of 's ancestors, so
By linearity of expectation,
So is an unbiased estimator of the tree size — each node contributes expected value exactly , regardless of the tree's shape. (Its variance can be large for very irregular trees, which is why one averages several probes.)
Empirical confirmation. The exact node counts of the legal-partial-placement -queens tree (Problem 1's convention) are
| 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | |
|---|---|---|---|---|---|---|---|---|---|
| nodes | 17 | 54 | 153 | 552 | 2057 | 8394 | 35539 | 166926 | 856189 |
Averaging over many random probes converges to these: e.g. probes give for (true ) and for (true ) — matching to within sampling error, as the unbiasedness proof predicts. A handful of probes suffices to predict, within a small factor, the cost of a search that would take far longer to run in full.
Your solutions
Use this space to log your own work — a restated problem, your approach, and how it compared with the sketch above.
Exercise N (rating RR)
Approach. Answer / proof. Compared with the sketch: what you did differently, what you missed.