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”- 1Gray CodeWalkthroughMediumBasic Anchor·LeetCode #89
- 2Maximum Subset XORWalkthroughHardSlight Variation·GeeksforGeeks
- 3Travelling Salesman (bitmask DP)WalkthroughHardInterview-Level·GeeksforGeeks
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?