60.bitmasking - NOTES
60.bitmasking - NOTES
Introduction
Bitmasking is when bits are used as a mask. To be more elaborate, when we use a
collection of bits and in that collection, the value of the bits and the different combinations
that can be formed is used to denote something relevant in a problem.
For example, in the problem to find the maximum sum of subset array bitmasking can be
applied, where each subset could be represented as a combination of bits.
Here, The length of the mask is equal to the length of the given array, and if the ith bit is set,
that means the ith element of the array is included in the subset.
For an array of size N, there is a total of 2N subsets thus there can be 2N masks.
1
Operations in Bitmasks
We will now see how operations like addition, deletion and checking for an element
in the set are performed using bitmasks.
Addition: We assume that the element is already not present in the set. To add the
ith element to the set, we need to set the ith bit of our mask to be one. This can be
done by setting the ith bit of the mask to be 1 by taking a bitwise or with (1<<i).
For example: Consider our set is {1, 2, 3} and our subset given is {1, 3}. If we want
to add the number 2 to the set, i.e make the 1st position of our mask to show 1,
then we have,
mask = 5 (101)2, k = 1
Note: mask represents our already present subset and new_mask represents our
new subset after addition.
Deletion: We assume that the element is already present in the set. To remove the
ith element from the set, we need to unset the ith bit of our mask. This can be done
by setting the ith bit of the mask to be 0 by taking a bitwise xor with (1<<i).
For example:
Consider our set is {1, 2, 3} and our subset given is {1, 3}. If we want to remove the
number 3 from the set, i.e make the 2nd position of our mask to show 0, then we
have, mask = 5 (101)2, k = 2
2
Checking for an element in the set: To check if an element is present in the given
subset or not, we need to check if the ith bit of the bitmask is 1 or zero, where i is
the position of the element in the ordered set. We can use bitwise AND operations
to check this efficiently by taking bitwise AND with (1<<i).
For example:
Consider our set is {1, 2, 3} and our subset given is {1, 3}. If we want to check if the
number 3 is present in the set or not, then we have,
mask = 5 (101)2, k = 2
So, let n = (1 << k) = 2^k = 4 = (100)2
x & n = (101)2 & (100)2 = (100)2 = 4.
Since the result of the bitwise AND is not 0, the 3rd bit of x is set and number 3 is
present in the subset.
Subset Sum
Problem Statement: Given an integer array 'ARR' of size 'N' and an integer 'K',
return all the subsets of 'ARR' which sum to 'K'. Subset of an array 'ARR' is a tuple
that can be obtained from 'ARR' by removing some (possibly all) elements of 'ARR'.
Approach: One of the standard and useful method of finding the power set is
through bit-masking. Consider a number with N-bits in its binary representation, if
we consider that the state of ith bit depicts whether the ith array element is
included in the current subset or not, then we can uniquely identify one of the
subsets (as each number has a different binary representation). Now we can simply
iterate from 1 to 2n-1, each number in this iteration will define a subset uniquely.
To generate the subset just check for the bits that are ON in binary representation
3
on the number, and for each ON bit, we will simply include an array element
corresponding to its position.
Algorithm:
● Create a vector of vectors to store all the subsets which sum up to k.
● Create a function “findSubsetsThatSumToK” to return the ans.
● Run a loop from mask = 0 to 2N-1
○ Initialize “sum” = 0
○ Create a vector “subset” to store the subset of the array
○ Run an inner loop from i=0 to n-1
■ If the ith bit in the mask has a value equal to 1 then include ith
element of the array in the current subset.
○ If sum becomes equal to the given K, then ans.push_back(subset)
● Return ans
Code:
#include<bits/stdc++.h>
using namespace std;
4
if(sum == k) ans.push_back(subset);
}
return ans;
}
int main() {
Time Complexity
O((2 ^ N) * N), where ‘N’ is the number of elements in the integer array ‘ARR’.
Total 2 ^ N subsets are possible for an integer array ‘ARR’ of size ‘N’, with a subset
having a maximum size as ‘N’.
Space Complexity
O((2 ^ N) * N), where ‘N’ is the number of elements in the integer array ‘ARR’.
Total 2 ^ N subsets are possible for an integer array 'ARR' of size ‘N’, with a subset
having a maximum size as ‘N’.
5
Dynamic programming & BitMasking
Bit operations provide an efficient and convenient way to implement dynamic
Candy
Example: Let's suppose that there are 3 students and 3 candies and all of the
students like all the candies, so, the candies can be distributed in the following 6
ways:
6
Approach: We will be using a mix of dynamic programming and bit masking to
solve this problem.
Algorithm:
● Create a likes array which will contain the masks i.e, representing whether
the ith student likes jth candy or not.
● Create a 2-dimensional dp array dp[n+1][1<<n]
● Initialize dp[0][0]=1 // Base Case i.e, all the candies are given
7
● Run a loop from i=0 to i<n+1
○ Run a loop from mask=0 to mask<(1<<n)
○ If there is any candy left that current student like i.,e if ((likes[i]
&mask)>0)
○ Run a loop from j=0 to j<n
○ Check if candy is available and also the current student likes the candy
○ Give the current candy and update the dp array
● Return dp[n][(1<<n)-1]
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
int t;
cin>>t;
while(t--) {
int n;
cin>>n;
int likes[n+1];
for(int i=1; i<=n; i++) {
int mask = 0;
for(int j=0; j<n; j++) {
int pref;
cin>>pref;
if(pref == 1) {
mask |= (1<<j);
}
}
likes[i] = mask;
}
int dp[n+1][1<<n];
for(int i=0; i<=n; i++) {
for(int j=0; j<(1<<n); j++) {
dp[i][j] = 0;
8
}
}
dp[0][0] = 1;
cout<<dp[n][(1<<n) - 1]<<"\n";
}
return 0;
}
Time Complexity
Space Complexity