Given two arrays A[] and B[] of sizes N, the task is to find the maximum sum of Bitwise AND of same-indexed elements in the arrays A[] and B[] that can be obtained by rearranging the array B[] in any order.
Examples:
Input: A[] = {1, 2, 3, 4}, B[] = {3, 4, 1, 2}
Output: 10
Explanation: One possible way is to obtain the maximum value is to rearrange the array B[] to {1, 2, 3, 4}.
Therefore, sum of Bitwise AND of same-indexed elements of the arrays A[] and B[] = { 1&1 + 2&2 + 3&3 + 4&4 = 10), which is the maximum possible.Input: A[] = {3, 5, 7, 11}, B[] = {2, 6, 10, 12}
Output: 22
Naive Approach: The simplest approach to solve the problem is to generate all possible permutations of array B[] and for each permutation, calculate the sum of Bitwise AND of same-indexed elements in arrays A[] and B[] and update the maximum possible sum accordingly. Finally, print the maximum sum possible.
Time Complexity: O(N! * N)
Auxiliary Space: O(1)
Efficient Approach: The above approach can also be optimized based on the following observations:
- For each array element of A[] the idea is to chose a not selected array element of B[] using bitmasking which will give maximum bitwise AND sum upto the current index.
- The idea is to use Dynamic Programming with bitmasking as it has overlapping subproblems and optimal substructure.
- Suppose, dp(i, mask) represents the maximum bitwise AND sum of array A[] and i, with the selected elements of array B[] represented by bits-position of mask.
- Then the transition from one state to another state can be defined as:
- For all j in the range [0, N]:
- If the jth bit of mask is not set then, dp(i, mask) = max(dp(i, mask|(1<<j))).
Follow the steps below to solve the problem:
- Define a vector of vectors, says dp of dimension N*2N with value -1 to store all dp-states.
- Define a recursive Dp function say maximizeAndUtil(i, mask) to find the maximum sum of the bitwise AND of the elements at the same respective positions in both arrays A[] and B[]:
- In the base case, if i is equal to N then return 0.
- If dp[i][mask] is not equal to -1 i.e already visited then return dp[i][mask].
- Iterate over the range [0, N-1] using variable j and in each iteration, If jth bit in the mask is not set then update dp[i][mask] as dp[i][mask] = max(dp[i][mask], maximizeUtil(i+1, mask| 2j).
- Finally, return dp[i][mask].
- Call the recursive function maximizeAnd(0, 0) and print the value returned by it as the answer.
Below is the implementation of the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Function to implement recursive DP int maximizeAnd( int i, int mask, int * A, int * B, int N, vector<vector< int > >& dp) { // If i is equal to N if (i == N) return 0; // If dp[i][mask] is not // equal to -1 if (dp[i][mask] != -1) return dp[i][mask]; // Iterate over the array B[] for ( int j = 0; j < N; ++j) { // If current element // is not yet selected if (!(mask & (1 << j))) { // Update dp[i][mask] dp[i][mask] = max( dp[i][mask], (A[i] & B[j]) + maximizeAnd(i + 1, mask | (1 << j), A, B, N, dp)); } } // Return dp[i][mask] return dp[i][mask]; } // Function to obtain maximum sum // of Bitwise AND of same-indexed // elements from the arrays A[] and B[] int maximizeAndUtil( int * A, int * B, int N) { // Stores all dp-states vector<vector< int > > dp( N, vector< int >(1 << N + 1, -1)); // Returns the maximum value // returned by the function maximizeAnd() return maximizeAnd(0, 0, A, B, N, dp); } // Driver Code int main() { int A[] = { 3, 5, 7, 11 }; int B[] = { 2, 6, 10, 12 }; int N = sizeof A / sizeof A[0]; cout << maximizeAndUtil(A, B, N); } |
Java
// Java program for the above approach import java.io.*; import java.lang.*; import java.util.*; public class GFG { // Function to implement recursive DP static int maximizeAnd( int i, int mask, int A[], int B[], int N, int [][] dp) { // If i is equal to N if (i == N) return 0 ; // If dp[i][mask] is not // equal to -1 if (dp[i][mask] != - 1 ) return dp[i][mask]; // Iterate over the array B[] for ( int j = 0 ; j < N; ++j) { // If current element // is not yet selected if ((mask & ( 1 << j)) == 0 ) { // Update dp[i][mask] dp[i][mask] = Math.max( dp[i][mask], (A[i] & B[j]) + maximizeAnd(i + 1 , mask | ( 1 << j), A, B, N, dp)); } } // Return dp[i][mask] return dp[i][mask]; } // Function to obtain maximum sum // of Bitwise AND of same-indexed // elements from the arrays A[] and B[] static int maximizeAndUtil( int A[], int B[], int N) { // Stores all dp-states int dp[][] = new int [N][( 1 << N) + 1 ]; for ( int dd[] : dp) Arrays.fill(dd, - 1 ); // Returns the maximum value // returned by the function maximizeAnd() return maximizeAnd( 0 , 0 , A, B, N, dp); } // Driver Code public static void main(String[] args) { int A[] = { 3 , 5 , 7 , 11 }; int B[] = { 2 , 6 , 10 , 12 }; int N = A.length; System.out.print(maximizeAndUtil(A, B, N)); } } // This code is contributed by Kingash. |
Python3
# Python3 program for the above approach # Function to implement recursive DP def maximizeAnd(i, mask, A, B, N, dp): # If i is equal to N if (i = = N): return 0 # If dp[i][mask] is not # equal to -1 if (dp[i][mask] ! = - 1 ): return dp[i][mask] # Iterate over the array B[] for j in range (N): # If current element # is not yet selected if ((mask & ( 1 << j)) = = 0 ): # Update dp[i][mask] dp[i][mask] = max ( dp[i][mask],(A[i] & B[j]) + maximizeAnd(i + 1 , mask | ( 1 << j), A, B, N, dp)) # Return dp[i][mask] return dp[i][mask] # Function to obtain maximum sum # of Bitwise AND of same-indexed # elements from the arrays A[] and B[] def maximizeAndUtil(A, B, N): # Stores all dp-states temp = [ - 1 for i in range ( 1 << N + 1 )] dp = [temp for i in range (N)] # Returns the maximum value # returned by the function maximizeAnd() return maximizeAnd( 0 , 0 , A, B, N, dp) # Driver Code if __name__ = = '__main__' : A = [ 3 , 5 , 7 , 11 ] B = [ 2 , 6 , 10 , 12 ] N = len (A) print (maximizeAndUtil(A, B, N)) # This code is contributed by ipg2016107 |
C#
// C# program for the above approach using System; class GFG { // Function to implement recursive DP static int maximizeAnd( int i, int mask, int [] A, int [] B, int N, int [,] dp) { // If i is equal to N if (i == N) return 0; // If dp[i][mask] is not // equal to -1 if (dp[i, mask] != -1) return dp[i, mask]; // Iterate over the array B[] for ( int j = 0; j < N; ++j) { // If current element // is not yet selected if ((mask & (1 << j)) == 0) { // Update dp[i][mask] dp[i, mask] = Math.Max( dp[i, mask], (A[i] & B[j]) + maximizeAnd(i + 1, mask | (1 << j), A, B, N, dp)); } } // Return dp[i][mask] return dp[i, mask]; } // Function to obtain maximum sum // of Bitwise AND of same-indexed // elements from the arrays A[] and B[] static int maximizeAndUtil( int [] A, int [] B, int N) { // Stores all dp-states int [,] dp = new int [N, (1 << N) + 1]; for ( int i = 0; i<N; i++) { for ( int j =0 ; j<(1 << N) + 1; j++) { dp[i, j] = -1; } } // Returns the maximum value // returned by the function maximizeAnd() return maximizeAnd(0, 0, A, B, N, dp); } // Driver Code static void Main() { int [] A = { 3, 5, 7, 11 }; int [] B = { 2, 6, 10, 12 }; int N = A.Length; Console.Write(maximizeAndUtil(A, B, N)); } } // This code is contributed by sanjoy_62. |
Javascript
<script> // Javascript program for the above approach // Function to implement recursive DP function maximizeAnd(i, mask, A, B, N, dp) { // If i is equal to N if (i == N) return 0; // If dp[i][mask] is not // equal to -1 if (dp[i][mask] != -1) return dp[i][mask]; // Iterate over the array B[] for ( var j = 0; j < N; ++j) { // If current element // is not yet selected if (!(mask & (1 << j))) { // Update dp[i][mask] dp[i][mask] = Math.max( dp[i][mask], (A[i] & B[j]) + maximizeAnd(i + 1, mask | (1 << j), A, B, N, dp)); } } // Return dp[i][mask] return dp[i][mask]; } // Function to obtain maximum sum // of Bitwise AND of same-indexed // elements from the arrays A[] and B[] function maximizeAndUtil(A, B, N) { // Stores all dp-states var dp = Array.from( Array(N), () => Array(1 << N + 1).fill(-1)); // Returns the maximum value // returned by the function maximizeAnd() return maximizeAnd(0, 0, A, B, N, dp); } // Driver Code var A = [ 3, 5, 7, 11 ]; var B = [ 2, 6, 10, 12 ]; var N = A.length document.write(maximizeAndUtil(A, B, N)); // This code is contributed by rrrtnx </script> |
22
Time Complexity: O (N2 * 2N)
Auxiliary Space: O(N * 2N)
DP Tabulation Approach(Iterative approach): The approach to solving this problem is the same but the DP tabulation(bottom-up) method is better than the Dp + memoization(top-down) because the memoization method needs extra stack space of recursion calls. Below are the steps:
- Create a 2D matrix, say DP[][] to store the solution of the subproblems and initialize it with 0.
- Initialize the DP with base cases.
- Now Iterate over subproblems to get the value of the current problem from the previous computation of subproblems stored in the DP[][] as the transition from one state to another state can be defined as:
- For all j in the range [0, N], If the jth bit of mask is not set then, dp(i, mask) = max(dp(i, mask|(1<<j))).
- Return the final solution stored in dp[0][(1 << N) – 1].
Implementation:
C++
#include <bits/stdc++.h> using namespace std; // Function to obtain maximum sum // of Bitwise AND of same-indexed // elements from the arrays A[] and B[] int maximizeAndUtil( int * A, int * B, int N) { // dp[i][mask] stores the maximum sum of Bitwise AND // of first i elements of A and jth element of B, // where each element of B can be used at most once and // the set of already used elements is represented by // the binary mask 'mask'. vector<vector< int > > dp(N + 1, vector< int >(1 << N, 0)); // dp[N][mask] is initialized to 0 for all masks // as Bitwise AND of empty sets is 0. for ( int mask = 0; mask < (1 << N); mask++) { dp[N][mask] = 0; } // i ranges from N-1 to 0 for ( int i = N - 1; i >= 0; i--) { // j ranges from 0 to N-1 for ( int j = 0; j < N; j++) { // If the j-th element of B is // not already used if ((1 << j) & ((1 << N) - 1)) { // Iterate over all possible // sets of elements of B // that can be used along // with the j-th element for ( int mask = 0; mask < (1 << N); mask++) { if ((1 << j) & mask) { dp[i][mask] = max( dp[i][mask], (A[i] & B[j]) + dp[i + 1] [mask ^ (1 << j)]); } } } } } // The maximum sum is stored // in dp[0][(1 << N) - 1] return dp[0][(1 << N) - 1]; } // Driver Code int main() { int A[] = { 3, 5, 7, 11 }; int B[] = { 2, 6, 10, 12 }; int N = sizeof A / sizeof A[0]; cout << maximizeAndUtil(A, B, N); } |
Java
import java.util.*; // Added by ~Nikunj Sonigara public class Main { public static int maximizeAndUtil( int [] A, int [] B, int N) { int [][] dp = new int [N + 1 ][ 1 << N]; for ( int mask = 0 ; mask < ( 1 << N); mask++) { dp[N][mask] = 0 ; } for ( int i = N - 1 ; i >= 0 ; i--) { for ( int j = 0 ; j < N; j++) { if (( 1 << j & ( 1 << N) - 1 ) != 0 ) { for ( int mask = 0 ; mask < ( 1 << N); mask++) { if (( 1 << j & mask) != 0 ) { dp[i][mask] = Math.max(dp[i][mask], (A[i] & B[j]) + dp[i + 1 ][mask ^ ( 1 << j)]); } } } } } return dp[ 0 ][( 1 << N) - 1 ]; } public static void main(String[] args) { int [] A = { 3 , 5 , 7 , 11 }; int [] B = { 2 , 6 , 10 , 12 }; int N = A.length; System.out.println(maximizeAndUtil(A, B, N)); } } |
Python3
# Added by ~Nikunj Sonigara def maximizeAndUtil(A, B, N): dp = [[ 0 ] * ( 1 << N) for _ in range (N + 1 )] for mask in range ( 1 << N): dp[N][mask] = 0 for i in range (N - 1 , - 1 , - 1 ): for j in range (N): if ( 1 << j) & (( 1 << N) - 1 ): for mask in range ( 1 << N): if ( 1 << j) & mask: dp[i][mask] = max (dp[i][mask], (A[i] & B[j]) + dp[i + 1 ][mask ^ ( 1 << j)]) return dp[ 0 ][( 1 << N) - 1 ] if __name__ = = "__main__" : A = [ 3 , 5 , 7 , 11 ] B = [ 2 , 6 , 10 , 12 ] N = len (A) print (maximizeAndUtil(A, B, N)) |
C#
using System; public class MaximizeAndUtilMain { public static int MaximizeAndUtil( int [] A, int [] B, int N) { // Initialize a 2D array to store the dynamic programming results. int [,] dp = new int [N + 1, 1 << N]; // Initialize the last row of the dynamic programming table to 0. for ( int mask = 0; mask < (1 << N); mask++) { dp[N, mask] = 0; } // Start filling the dynamic programming table from the second-to-last row, going backwards. for ( int i = N - 1; i >= 0; i--) { // Iterate through all elements in array B. for ( int j = 0; j < N; j++) { // Check if the j-th element in array B is included in the current mask. if ((1 << j & ((1 << N) - 1)) != 0) { // Iterate through all possible masks. for ( int mask = 0; mask < (1 << N); mask++) { // Check if the j-th element is included in the current mask. if ((1 << j & mask) != 0) { // Calculate the maximum of the current dp value and the bitwise AND of A[i] and B[j] // plus the value from the next row and the mask with j-th bit turned off. dp[i, mask] = Math.Max(dp[i, mask], (A[i] & B[j]) + dp[i + 1, mask ^ (1 << j)]); } } } } } // The result is stored in the top-left cell of the dynamic programming table. return dp[0, (1 << N) - 1]; } public static void Main() { int [] A = { 3, 5, 7, 11 }; int [] B = { 2, 6, 10, 12 }; int N = A.Length; // Call the MaximizeAndUtil function and print the result. Console.WriteLine(MaximizeAndUtil(A, B, N)); } } |
Javascript
function maximizeAndUtil(A, B, N) { // dp[i][mask] stores the maximum sum of Bitwise AND // of first i elements of A and jth element of B, // where each element of B can be used at most once and // the set of already used elements is represented by // the binary mask 'mask'. const dp = new Array(N + 1).fill(0).map(() => new Array(1 << N).fill(0)); // dp[N][mask] is initialized to 0 for all masks // as Bitwise AND of empty sets is 0. for (let mask = 0; mask < (1 << N); mask++) { dp[N][mask] = 0; } // i ranges from N-1 to 0 for (let i = N - 1; i >= 0; i--) { // j ranges from 0 to N-1 for (let j = 0; j < N; j++) { // If the j-th element of B is // not already used if ((1 << j) & ((1 << N) - 1)) { // Iterate over all possible // sets of elements of B // that can be used along // with the j-th element for (let mask = 0; mask < (1 << N); mask++) { if ((1 << j) & mask) { dp[i][mask] = Math.max( dp[i][mask], (A[i] & B[j]) + dp[i + 1][mask ^ (1 << j)] ); } } } } } // The maximum sum is stored // in dp[0][(1 << N) - 1] return dp[0][(1 << N) - 1]; } // Driver code const A = [3, 5, 7, 11]; const B = [2, 6, 10, 12]; const N = A.length; console.log(maximizeAndUtil(A, B, N)); |
22
Time Complexity: O (N2 * 2N)
Auxiliary Space: O(N * 2N)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!