Lesson 8.81,554 words

Bitmask DP

When a subproblem depends not on an index or a prefix but on which subset of a small ground set has been used, we can encode that subset as the bits of an integer and index a DP table by it. With n20n \le \sim 20 the 2n2^n subsets fit in a table, turning $\Theta(n!

╌╌╌╌

Every dynamic program so far has indexed its subproblems by something ordered: a prefix of an array, a position in a string, a capacity remaining. But some problems have no useful order. The traveling salesman does not care in what sequence you list the cities he has already visited; what matters is only the set of visited cities and where he stands now. The natural subproblem state is therefore a subset of the ground set, and a DP over subsets seems to need a table indexed by subsets, of which there are .

A subset of an -element set is exactly an -bit string, which is exactly an integer in . So we encode the subset as the bits of an integer mask and index the DP table by that integer. When the masks (up to about a million) fit comfortably in memory, and a exhaustive search collapses to .1 This encoding builds on the general principles of dynamic programming, and unlocks a handful of recurring DP shapes.

Bit tricks, compactly

A mask is an integer whose bit (counting from , least significant) is iff element is in the set. The operations we need are all :

  • test whether : (mask >> i) & 1.
  • add : mask | (1 << i); remove : mask & ~(1 << i).
  • isolate the lowest set bit: mask & -mask (two's complement makes , which agrees with mask only at and below the lowest ).
  • popcount (number of set bits): a hardware instruction, or 's loop while (m) { m &= m - 1; c++; }, which clears the lowest set bit each step.
  • iterate the members: for from to , test bit .
  • the full set is (1 << n) - 1; the empty set is 0.

The whole universe of subsets is the integers , so a loop for (mask = 0; mask < (1 << n); mask++) visits every subset once. Because mask | (1 << j) > mask whenever bit was unset, masks that add an element are numerically larger, so iterating masks in increasing order processes every subset after all of its proper subsets, which is exactly the topological order a subset DP needs.

Subset lattice for : each edge adds one bit, so — increasing integer order visits every subset after its proper subsets

Held–Karp: the traveling salesman archetype

The cleanest bitmask DP is Held–Karp for the traveling salesman problem, which is itself NP-hard so no polynomial algorithm is known. Given cities and pairwise distances , find the shortest tour that starts at city , visits every city exactly once, and returns to . Brute force tries all orderings. The DP observation is that a partial tour is fully summarized by which cities it has visited and where it currently ends; the order in which the visited cities were reached is irrelevant to how cheaply we can finish.

This is where the exponential collapse happens: many distinct visit orders reach the very same , and the DP keeps only the cheapest, solving each state once instead of re-exploring every permutation.

Two visit orders, one state. Both and visit and end at , so Held–Karp folds them into the single state and keeps only the cheaper, collapsing the factorial of orders.
= best path visiting the cities in mask, ending at , extended to a new city

The recurrence extends a path ending at to a new, previously-unvisited city by setting bit :

The base case is . The answer closes the tour back to the start over the full mask :

Algorithm:Held-Karp(d,n)\textsc{Held-Karp}(d, n) — shortest tour by bitmask DP, O(2nn2)O(2^n n^2)
  1. 1
    dp[mask][i]dp[\text{mask}][i] \gets \infty for all mask, ii
  2. 2
    dp[1][0]0dp[1][0] \gets 0
    only city 00 visited, at 00
  3. 3
    for mask1\text{mask} \gets 1 to 2n12^n - 1 do
  4. 4
    for i0i \gets 0 to n1n - 1 do
  5. 5
    if dp[mask][i]=dp[\text{mask}][i] = \infty then continue
  6. 6
    if mask\text{mask} does not contain ii then continue
  7. 7
    for j0j \gets 0 to n1n - 1 do
  8. 8
    if mask\text{mask} contains jj then continue
    visited
  9. 9
    nmaskmask(1j)\text{nmask} \gets \text{mask} \mid (1 \ll j)
  10. 10
    dp[nmask][j]min(dp[nmask][j], dp[mask][i]+d(i,j))dp[\text{nmask}][j] \gets \min\bigl(dp[\text{nmask}][j],\ dp[\text{mask}][i] + d(i,j)\bigr)
  11. 11
    return mini(dp[2n1][i]+d(i,0))\textbf{return } \min_i \bigl(dp[2^n - 1][i] + d(i, 0)\bigr)

Iterating masks in increasing order is valid because , so every state is finalized before it is read. The table has entries and each is relaxed by an inner loop over candidate predecessors, giving time and space. That is exponential, but at is about , entirely feasible, whereas is not.

This is exactly Shortest Path Visiting All Nodes: take for graph edges and run the same as a BFS over states , where mask is the set of nodes visited so far and the answer is the first time any state with is reached. Unlike the single-source shortest paths of weighted graphs, here the path must cover every node, which is what forces the subset state.

Assignment and matching by mask

The same idea solves the assignment problem: assign tasks to workers, where worker doing task costs , minimizing total cost. Here the mask tracks which tasks have been assigned. The key observation is that if we always assign workers in order , then once mask is fixed the number of workers already placed is determined: it is exactly .

The transition gives worker each task not yet used:

Assignment by mask: with two tasks are taken, so fixes worker as next, branching to a free task ( or )

With base and answer , there are states each with transitions: time, space, a clean factor of cheaper than Held–Karp because the current position dimension is replaced by the free information . Maximum Students Taking Exam is a row-by-row variant: the per-row state is a bitmask of seated columns, and a row's choice is constrained by the previous row's mask plus broken seats.

Subset-sum partitioning over masks

Partition to K Equal Sum Subsets asks whether a multiset can be split into groups of equal sum . Treat the chosen elements as a mask and carry just enough to know how the current group is doing.

Concretely stores the used capacity of the current bucket, defined when mask is reachable. From a reachable mask with partial fill , we may add any element whose value keeps the bucket within :

where hitting exactly rolls over to and opens a fresh bucket. The whole set is partitionable iff (every bucket closed exactly). This is , the same shape as assignment: masks, candidate elements per mask, feasibility check.

Submask enumeration and the bound

Some subset DPs need, for each mask, to consider every way of splitting it into two complementary parts, for instance partitioning a set of cities into groups each served by one route. That requires iterating over all submasks of a given mask. The idiom is:

Algorithm:enumerate every submask submask\text{sub} \subseteq \text{mask}
  1. 1
    submask\text{sub} \gets \text{mask}
  2. 2
    while sub>0\text{sub} > 0 do
  3. 3
    // ... use sub and its complement ...
  4. 4
    sub(sub1)&mask\text{sub} \gets (\text{sub} - 1) \mathbin{\&} \text{mask}
  5. 5
    // empty submask 0 handled after loop

Subtracting from sub borrows through its trailing zeros; & mask then snaps the result back inside mask, so the sequence steps through the submasks in strictly decreasing order down to . The cost across all masks is the striking part:

a mask, its submasks via the step, and the three roles each element plays

So a DP that, for every mask, loops over all its submasks runs in , not the a naive all masks all masks bound would suggest. Find the Shortest Superstring and Minimum Cost to Connect Two Groups of Points are submask/mask DPs of this flavor: the latter builds the answer by, for each mask of right-group points, choosing how to cover it given a left point. When the transition is a sum (or min/max) over all submasks of each mask with a fixed contribution per submask, there is an even faster technique, SOS DP (sum over subsets), covered in the DP-optimizations lesson, that beats the enumeration by sharing work across masks.2

Takeaways

  • Bitmask DP applies when a subproblem's state is which subset of a small ground set () has been used; encode the subset as the bits of an integer and index the table by that mask, giving states.
  • The core bit operations (test (mask >> i) & 1, set mask | (1<<i), clear mask & ~(1<<i), lowest bit mask & -mask, popcount) are all , and iterating masks in increasing order is a valid topological order for subset DPs.
  • Held–Karp solves TSP with = best path visiting mask ending at , in time and space versus brute force, and is Shortest Path Visiting All Nodes.
  • Assignment and subset-sum partitioning use alone in , exploiting to recover the implicit position or bucket state.
  • Submask enumeration via sub = (sub - 1) & mask iterates every submask of every mask in total , because each element is independently in sub, in the complement, or in neither.

Footnotes

  1. Erickson, Ch. — Dynamic Programming: exponential subset DPs trade exhaustive search for by memoizing over subsets.
  2. CLRS, Ch. 15 — Dynamic Programming (§15.3): optimal substructure and overlapping subproblems — the two ingredients that justify indexing subproblems by subset masks.
Practice

╌╌ END ╌╌