2Brute2Best
🔥0
04

Bitmask state (Bit Manipulation Sub-pattern)

An integer stands for a set of items (bit i = item i chosen) or for a code to be generated. With up to ~20 items, you can loop over or run DP across all 2^n sets.

Signals in the problem statement
  • ›

    n ≤ 15–20 and "visit every city / node once"

  • ›

    "choose any subset" to maximise XOR / sum

  • ›

    "Gray code", sequences where neighbours differ in one bit

  • ›

    assigning n workers to n jobs, n small

What makes it different

Generating codes → a formula on i (i ^ (i >> 1)). Subset XOR → an XOR basis, one number per leading bit. Visit-all / assignment → dp[mask][last] or dp[mask], masks in increasing order.

Questions

ordered from “learn the idea” to “recognise it without help”
  1. 1
    Gray Code
    Basic Anchor·LeetCode #89
    Medium
  2. 2
    Maximum Subset XOR
    Slight Variation·GeeksforGeeks
    Hard
  3. 3
    Travelling Salesman (bitmask DP)
    Interview-Level·GeeksforGeeks
    Hard

Template

bitmask-template.cpp
// A mask is a set: bit i = item i is in. n up to ~20.
//   in:  (mask >> i) & 1     add: mask | (1 << i)     size: __builtin_popcount(mask)
void forEachSubset(int n) {
    for (int mask = 0; mask < (1 << n); mask++) {
        for (int i = 0; i < n; i++)
            if ((mask >> i) & 1) { /* item i is in this subset */ }
    }
}

// Visit-all DP (TSP shape): dp[mask][last] = best cost to start at 0,
// visit exactly the items in mask, and stand at last.
int visitAll(const vector<vector<int>>& cost) {
    int n = cost.size(), FULL = (1 << n) - 1;
    const int INF = 1e9;
    vector<vector<int>> dp(1 << n, vector<int>(n, INF));
    dp[1][0] = 0;                             // only item 0 visited, standing at 0
    for (int mask = 1; mask <= FULL; mask++)  // smaller masks are always finished first
        for (int last = 0; last < n; last++) {
            if (dp[mask][last] == INF) continue;
            for (int nxt = 0; nxt < n; nxt++) {
                if ((mask >> nxt) & 1) continue;
                int nm = mask | (1 << nxt);
                dp[nm][nxt] = min(dp[nm][nxt], dp[mask][last] + cost[last][nxt]);
            }
        }
    int best = INF;
    for (int last = 0; last < n; last++) best = min(best, dp[FULL][last] + cost[last][0]);
    return n == 1 ? 0 : best;
}

// Every submask of mask (for "split a set into two parts" DPs): 3^n in total.
void forEachSubmask(int mask) {
    for (int sub = mask; sub > 0; sub = (sub - 1) & mask) { /* use sub */ }
}
Remember this · Bit Manipulation
Trigger
"Every element appears twice except…", power of two, counting set bits, subsets as bitmasks, O(1) space tricks.
Think
What happens to this problem if I look at each bit separately?
Key question
Which bit operation (XOR, AND, OR, shift) cancels or isolates what I need?