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 the 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 withmaskonly 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 is0.
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.
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.
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 :
- 1for all mask,
- 2only city visited, at
- 3for to do
- 4for to do
- 5if then continue
- 6if does not contain then continue
- 7for to do
- 8if contains then continuevisited
- 9
- 10
- 11
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:
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:
- 1
- 2while do
- 3// ... use sub and its complement ...
- 4
- 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:
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, setmask | (1<<i), clearmask & ~(1<<i), lowest bitmask & -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
maskending 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) & maskiterates every submask of every mask in total , because each element is independently insub, in the complement, or in neither.
Footnotes
╌╌ END ╌╌