Exercises — Bitmask DP — TSP intro
Throughout, cities are numbered , the tour starts and ends at city 0, and is the cost of the edge . The DP table is = cheapest path from city that has visited exactly the set mask and now stands at city .
Level 1 — Recognition
Exercise L1.1 — Read a mask
n = 5. The mask is the integer . Which cities are visited, and how many? Write the binary.
Recall Solution
A mask is just an integer whose binary digits are tick-boxes. Bit set ⟺ city visited.
. Binary over 5 bits (bit4…bit0): 10110.
Reading right-to-left: bit0=0, bit1=1, bit2=1, bit3=0, bit4=1.
Visited cities: — three cities.
Exercise L1.2 — Bit tests
With mask = 22 (as above), evaluate each expression and say what it means:
(a) mask & (1<<2) (b) mask & (1<<3) (c) mask | (1<<0) (d) mask & ~(1<<1).
Recall Solution
Recall 1<<i = "the integer with only bit on".
(a) 1<<2 = 4; 22 & 4 = 4 (non-zero) → city 2 IS visited.
(b) 1<<3 = 8; 22 & 8 = 0 → city 3 is NOT visited.
(c) 1<<0 = 1; 22 | 1 = 23 → add city 0, giving .
(d) ~(1<<1) clears bit1; 22 & ~2 = 20 → remove city 1, giving .
Exercise L1.3 — Total states
For general , how many entries does the table have, and how many are reachable (i.e. bit set in mask)? Give the count for .
Recall Solution
Masks range , so masks; current city has choices → table slots.
A slot is meaningful only if bit is set in mask (you can't "end at" an unvisited city). For a fixed , exactly half the masks have bit on: . Over all : reachable slots.
For : table slots, reachable .
Level 2 — Application
Exercise L2.1 — Base case
Write the full initial dp table for (list only the non- entry) and say why every other entry is .
Recall Solution
dp[1][0] = 0 — that is dp[001][0]: only city 0 visited, standing at 0, cost 0.
Everything else is because at the start there is no path that has already visited other cities. An entry literally means "this situation is not yet reachable." As the DP progresses these fill in.
Exercise L2.2 — Run 3 cities by hand
Distances (symmetric): .
Compute the full closed-tour TSP cost. Show every dp entry.
Recall Solution
Base: dp[001][0]=0.
Peel-the-last-edge transition: , mask & ~(1<<i).
| State | Compute | Value |
|---|---|---|
dp[011][1] |
dp[001][0]+dist[0][1]=0+5 |
5 |
dp[101][2] |
dp[001][0]+dist[0][2]=0+9 |
9 |
dp[111][2] |
dp[011][1]+dist[1][2]=5+4 |
9 |
dp[111][1] |
dp[101][2]+dist[2][1]=9+4 |
13 |
Close the cycle (add return edge to 0): Both orders and cost . Answer: 18. ✓
Exercise L2.3 — Reading a transition
, computing dp[mask][2] with mask = 0111 . List prev, the valid predecessors , and the full expression.
Recall Solution
Check endpoint is legal: bit2 of 0111 is 1 ✓ (city 2 is visited).
prev = 7 & ~(1<<2) = 0111 & 1011 = 0011 .
Valid predecessors are cities in prev: .
City 3 is not in prev (bit3 = 0), so it is not a candidate — you cannot have come from a city you never visited.
Level 3 — Analysis
Exercise L3.1 — Why not store the order?
A friend insists: "Two tours that visited ending at 2 but in different orders ( vs ... wait, that ends at 1) — surely the order changes the future." Rebut precisely: why can we collapse same-(set, spot) states?
Recall Solution
The remaining cost — the cost of finishing the tour — depends only on (a) which cities still need visiting (= complement of mask) and (b) where you currently stand (city ). Both are captured by . The internal order of the already-visited prefix affects only the prefix's own accumulated cost, which is already stored as the value .
So two paths reaching the same are interchangeable from here on; we keep the cheaper value and discard the other. This is the memorylessness that makes the state small: states instead of orderings. See figure below.

Exercise L3.2 — Count the work
For , compute (i) the number of states, (ii) roughly the total transitions , and (iii) compare to . State the ratio order-of-magnitude.
Recall Solution
(i) States . (ii) Transitions (about ) — trivial for a computer. (iii) . Ratio . Bitmask DP does about 4500× less work here — and the gap widens explosively with .
Exercise L3.3 — Where does optimal substructure come from?
State, in one clean argument, why appearing inside the min must itself be optimal (the exchange argument).
Recall Solution
Suppose the best path to ends with edge , and its prefix (a path to ) had cost . If there existed a cheaper path to of cost , we could glue edge onto it and obtain a path to of cost — beating the supposed best. Contradiction. Hence the prefix must already be optimal, i.e. equal to . This "cut-and-paste" is exactly the optimal-substructure property that legalises the recurrence.
Level 4 — Synthesis
Exercise L4.1 — Hamiltonian path, not cycle
Using the same distances as L2.2 (), find the cheapest Hamiltonian path (start at 0, visit all, no return). Which endpoint wins?
Recall Solution
Drop the return term; answer over all (including ? no — city 0 is the fixed start, so here since the path must end elsewhere after visiting all).
From L2.2: dp[111][1]=13, dp[111][2]=9.
, achieved by ending at city 2 via route .
This is cheaper than the cycle (18) precisely because we skipped the expensive return edge . This is the Hamiltonian Path and Cycle distinction in action.
Exercise L4.2 — Fixed start AND fixed end (path)
Now require the path to start at 0 and end at city 1, visiting all of , same distances. Cost?
Recall Solution
We want directly — no min over endpoints, because the endpoint is pinned to 1.
full = 111. From L2.2, dp[111][1] = 13 (route : ).
The only other way to end at 1 having visited all is ; the sole "middle" city is 2, so is forced. Answer: 13.
Lesson: pinning the endpoint means reading one table cell instead of minimising.
Exercise L4.3 — Asymmetric distances (one-way roads)
, but now directed: . Find the cheapest closed tour starting/ending at 0. Show both directions differ.
Recall Solution
Here dist[j][i] ≠ dist[i][j], so direction matters — this is why the transition uses dist[j][i] not dist[i][j].
| State | Compute | Value |
|---|---|---|
dp[011][1] |
dp[001][0]+d[0][1]=0+2 |
2 |
dp[101][2] |
dp[001][0]+d[0][2]=0+6 |
6 |
dp[111][2] |
dp[011][1]+d[1][2]=2+1 |
3 |
dp[111][1] |
dp[101][2]+d[2][1]=6+9 |
15 |
Close: .
Winning tour costs . The reverse . Directions differ wildly — answer 6, and only the DP's directional dist[j][i] catches this.
Level 5 — Mastery
Exercise L5.1 — Multiple start cities (metric closure trick)
You may start the tour at any city (still a closed cycle visiting all). Does fixing the start at 0 lose optimality? Prove your answer.
Recall Solution
No optimality is lost. A closed tour is a cycle; a cycle has no distinguished starting point — it visits the same edges regardless of which vertex you call "start." So the optimal cycle passes through city 0, and rotating our labelling to begin the traversal at 0 describes the same cycle with the same cost. Hence fixing start still finds the global optimum. (Caveat: this holds for the cycle version. For a path with a free start, you would loop the base case over all start cities, multiplying the work by .)
Exercise L5.2 — Count optimal tours
For the symmetric triangle of L2.2 (), how many distinct closed tours achieve the optimum, and why? Generalise: when does a symmetric instance have exactly 2 optimal tours vs 1?
Recall Solution
For there are exactly — precisely 2 directed closed tours from city 0: and . With symmetric distances both traverse the same three edges in opposite directions, so they always cost the same . Here that is , achieved by both tours. Generalisation: for symmetric , the two tours are mirror images and always tie — there are always exactly 2 optimal directed tours (or you may count them as 1 undirected tour). A strict single winner appears only when distances are asymmetric, breaking the mirror symmetry (as in L4.3, where 6 ≠ 22).
Exercise L5.3 — Memory halving via forward push
The reference implementation pushes forward (mask → nm = mask | (1<<k)). Argue why iterating masks in increasing order guarantees correctness, and give the largest for which an int-indexed dp array of longs fits in MB (assume 8 bytes/entry).
Recall Solution
Correctness of increasing order: the forward push updates dp[nm][k] from dp[mask][i] where nm = mask | (1<<k) and bit was not in mask. Setting a previously-zero bit makes nm > mask strictly. So every source mask is a smaller integer than its target — by the time we reach nm in the increasing loop, all masks (including every possible source) are already final. No state is read before it is finished. ✓
Memory bound: entries , each 8 bytes. Need bytes, i.e. .
- : ✓
- : ✗ Largest = 20. (This is the classic "" ceiling of the Held-Karp Algorithm; beyond it you switch to Branch and Bound or heuristics.)
Recall One-line self-check summary
Read a mask ::: decompose the integer into powers of two.
Endpoint legality ::: dp[mask][i] valid only if mask & (1<<i).
Peeled edge direction ::: always dist[j][i] (into the current city).
Cycle vs path ::: cycle adds +dist[i][0]; path does not.
Free vs pinned endpoint ::: free → min_i dp[full][i]; pinned → one cell.
Iteration order ::: increasing masks work because setting a bit only grows the mask.
Connections
- Travelling Salesman Problem, Held-Karp Algorithm — the exact problem and algorithm these drills implement.
- Bit Manipulation, Subset Enumeration — the mask machinery (L1).
- Dynamic Programming — optimal substructure (L3.3).
- Hamiltonian Path and Cycle — path vs cycle distinction (L4).
- Branch and Bound — what you reach for past (L5.3).