The state includes a subset, represented as an integer whose bits mark membership. Feasible up to -.

Budget

ComplexityFeasible
25
22-23
18-20
(submask enumeration)15-17
memory20 (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                // complement

Iterating submasks — total

for (int sub = mask; sub; sub = (sub - 1) & mask) { /* sub is a proper submask */ }
// include the empty submask by handling sub = 0 separately

Summed 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

TechniqueGain
Derive one dimension from popcountremoves a factor of from the state
Iterate submasks
SOS DP for subset-sum-over-subsets
Only the lowest unset bit matterswhen items are interchangeable, forces a canonical order and cuts the branching
Meet in the middle when the problem splits
Symmetry breakingfix that element 0 is in the first group
vector<int> instead of 2Dbetter 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

See also: Subset DP · SOS DP · Held-Karp