Bitwise Operations & Bitmask
Concept
Bitmask uses an integer's binary representation to encode a set of boolean states. Bit i being 1 means element i is 'selected'. This enables compact subset representation, fast set operations via bitwise operators, and efficient DP over subsets. Essential for problems with n <= 20 where you need to enumerate all 2^n subsets.
When to Use
- ▸Enumerating all subsets of a set (n <= 20)
- ▸Bitmask DP — TSP (Traveling Salesman), assignment problems
- ▸Representing visited states compactly
- ▸Set operations (union = OR, intersection = AND, toggle = XOR)
- ▸Checking powers of 2, counting set bits
Intuition
An integer with n bits can represent any subset of {0, 1, ..., n-1}. Bit i is 1 if element i is in the subset. Checking membership: mask & (1 << i). Adding element: mask | (1 << i). Removing: mask & ~(1 << i). Toggling: mask ^ (1 << i). To enumerate all subsets, loop mask from 0 to (1 << n) - 1. For bitmask DP, the state is the set of items already used, and you try adding each unused item.
Complexity
Time
O(2^n * n) for subset enumeration, O(2^n * n^2) for TSP DP
Space
O(2^n) for bitmask DP
Template Code
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
ios_base::sync_with_stdio(0), cin.tie(0);
// --- Bit Manipulation Basics ---
int x = 13; // binary: 1101
// Check if i-th bit is set
int i = 2;
if(x & (1 << i)) cout << "bit " << i << " is set\n";
// Set i-th bit
x = x | (1 << 1); // 1101 -> 1111 = 15
// Clear i-th bit
x = x & ~(1 << 0); // 1111 -> 1110 = 14
// Toggle i-th bit
x = x ^ (1 << 3); // 1110 -> 0110 = 6
// Count set bits
cout << __builtin_popcount(x) << " bits set\n";
// Check if power of 2
// (x & (x-1)) == 0 means power of 2
// --- Enumerate All Subsets ---
int n;
cin >> n;
vector<int> v(n);
for(int i = 0; i < n; i++) cin >> v[i];
for(int mask = 0; mask < (1 << n); mask++){
int sum = 0;
for(int i = 0; i < n; i++){
if(mask & (1 << i)){
sum += v[i];
}
}
// process subset with this sum
}
// --- Bitmask DP: Minimum cost to assign n tasks to n people ---
// dp[mask] = min cost to complete tasks in mask
// mask represents which tasks are done
//
// int n;
// cin >> n;
// vector<vector<int>> cost(n, vector<int>(n));
// for(int i = 0; i < n; i++)
// for(int j = 0; j < n; j++)
// cin >> cost[i][j];
//
// const int inf = 1e9+7;
// vector<int> dp(1 << n, inf);
// dp[0] = 0;
//
// for(int mask = 0; mask < (1 << n); mask++){
// if(dp[mask] == inf) continue;
// int person = __builtin_popcount(mask); // which person to assign next
// for(int task = 0; task < n; task++){
// if(mask & (1 << task)) continue; // task already done
// int newMask = mask | (1 << task);
// dp[newMask] = min(dp[newMask], dp[mask] + cost[person][task]);
// }
// }
//
// cout << dp[(1 << n) - 1] << "\n";
return 0;
}
// Sample Input:
// 4
// 3 1 4 2
//
// (enumerates all 16 subsets and computes their sums)