The state includes a subset, represented as an integer whose bits mark membership. Feasible up to -.
Budget
| Complexity | Feasible |
|---|---|
| 25 | |
| 22-23 | |
| 18-20 | |
| (submask enumeration) | 15-17 |
| memory | 20 (memory is usually the binding constraint) |
The idioms
mask & (1 << i) // is i in the set
mask | (1 << i) // add i
mask & ~(1 << i) // remove i
mask ^ (1 << i) // toggle i
__builtin_popcount(mask) // size of the set
__builtin_ctz(mask) // index of the lowest set bit
mask & (mask - 1) // remove the lowest set bit
mask & (-mask) // isolate the lowest set bit
(1 << n) - 1 // the full set
full ^ mask // complementIterating submasks — total
for (int sub = mask; sub; sub = (sub - 1) & mask) { /* sub is a proper submask */ }
// include the empty submask by handling sub = 0 separatelySummed over all masks this is , because each element is in the submask, in the mask-but-not-submask, or in neither. See Enumerating Submasks.
The classic problems
Assignment / minimum cost matching
dp[mask] = minimum cost to assign the first popcount(mask) people to the job set mask.
for (int mask = 0; mask < (1 << n); mask++) {
int i = __builtin_popcount(mask); // next person to assign
if (i == n) continue;
for (int j = 0; j < n; j++)
if (!(mask >> j & 1))
dp[mask | (1 << j)] = min(dp[mask | (1 << j)], dp[mask] + cost[i][j]);
}. Using popcount to derive the row index removes a whole dimension — a trick worth reusing.
TSP / Hamiltonian path
dp[mask][i] = shortest path visiting exactly mask, ending at . .
Set cover / partition into groups
dp[mask] = minimum groups covering mask; iterate submasks that form a valid group. .
for (int mask = 1; mask < (1 << n); mask++)
for (int sub = mask; sub; sub = (sub - 1) & mask)
if (valid[sub]) dp[mask] = min(dp[mask], dp[mask ^ sub] + 1);Counting perfect matchings in a bipartite graph
dp[mask] where the bit count gives the left vertex being matched — this is the permanent, computable in rather than .
Optimisations
| Technique | Gain |
|---|---|
Derive one dimension from popcount | removes a factor of from the state |
| Iterate submasks | |
| SOS DP | for subset-sum-over-subsets |
| Only the lowest unset bit matters | when items are interchangeable, forces a canonical order and cuts the branching |
| Meet in the middle | when the problem splits |
| Symmetry breaking | fix that element 0 is in the first group |
vector<int> instead of 2D | better cache behaviour at states |
The “lowest unset bit” trick is underused: in set-partitioning problems, always assign the smallest unassigned element next. This avoids generating the same partition in different orders.
Memory
int is 80 MB — usually over the limit. Options: use int not long long, process masks in increasing popcount and keep only the needed layers, or switch to the inclusion-exclusion formulation which is time and space.
When is just too big
- : meet in the middle
- , but the graph is sparse: try broken profile DP or tree decomposition
- The subsets have structure (intervals, prefixes): the “subset” dimension may collapse to a number
- Approximate answers acceptable: simulated annealing