Purpose: Solve the Travelling Salesman Problem exactly in time and memory — the classic bitmask DP. Also called the Bellman-Held-Karp algorithm.
Algorithm
- Fix vertex as the start.
- State:
dp[mask][i]= cheapest path that starts at , visits exactly the setmaskof vertices, and currently sits at . - Base:
dp[1][0] = 0(only vertex 0 visited, standing on it). - Transition: for every
mask, everymaskwith a finite value, and everymask:
- Answer: for a tour, or for an open path.
Code
const long long INF = 1e18;
long long tsp(int n, vector<vector<long long>>& w) {
vector<vector<long long>> dp(1 << n, vector<long long>(n, INF));
dp[1][0] = 0;
for (int mask = 1; mask < (1 << n); mask++) {
if (!(mask & 1)) continue; // must contain vertex 0
for (int i = 0; i < n; i++) {
if (!(mask >> i & 1) || dp[mask][i] == INF) continue;
for (int j = 0; j < n; j++) {
if (mask >> j & 1) continue;
int nm = mask | (1 << j);
dp[nm][j] = min(dp[nm][j], dp[mask][i] + w[i][j]);
}
}
}
long long best = INF;
int full = (1 << n) - 1;
for (int i = 1; i < n; i++)
if (dp[full][i] != INF) best = min(best, dp[full][i] + w[i][0]);
return best;
}Reconstruct the tour by storing a par[mask][i] array, or by walking backwards and re-deriving each predecessor.
Paradigm
Dynamic programming over subsets. The insight is that a partial tour’s future cost depends only on which vertices remain and where you are — not on the order you visited them in. That collapses orderings into states.
Complexity
- Time:
- Space: — this is usually the binding constraint
| states × transitions | memory (8-byte cells) | |
|---|---|---|
| 15 | 4 MB | |
| 18 | 38 MB | |
| 20 | 168 MB — too much | |
| 22 | 738 MB — no |
Proof of Correctness
Claim: dp[mask][i] is the minimum cost of a path from through exactly the vertices of mask, ending at .
Induction on popcount(mask). Base: dp[1][0] = 0 is trivially correct. Step: any optimal path realising dp[mask][i] has a well-defined second-to-last vertex . Deleting leaves a path over mask \ {i} ending at , which by the inductive hypothesis costs at least dp[mask ^ (1<<i)][k]. The transition considers exactly all such , so it takes the minimum over all possible optimal decompositions. Conversely every value the transition produces corresponds to a real path, so no under-counting occurs. ∎
Variants / Use Cases
- Path instead of cycle — drop the closing edge
- Fixed start and end — force the final vertex
- Bitmask DP generally — assignment problems, set cover, Hamiltonian counting, matching in small graphs
- Dreyfus-Wagner — the same subset-DP idea for Steiner trees
- Held-Karp lower bound — a different Held-Karp result: a 1-tree Lagrangian relaxation used to prune branch-and-bound TSP solvers on large instances
- Approximation instead — Christofides gives a guarantee in polynomial time for metric TSP
- Memory relief — iterate masks in increasing popcount and keep only two layers, or use
float/int32costs