Rearrange an array to maximize sum of Bitwise AND of same-indexed elements with another array
Last Updated :
19 Oct, 2023
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++
#include <bits/stdc++.h>
using namespace std;
int maximizeAnd( int i, int mask,
int * A, int * B, int N,
vector<vector< int > >& dp)
{
if (i == N)
return 0;
if (dp[i][mask] != -1)
return dp[i][mask];
for ( int j = 0; j < N; ++j) {
if (!(mask & (1 << j))) {
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];
}
int maximizeAndUtil( int * A, int * B, int N)
{
vector<vector< int > > dp(
N, vector< int >(1 << N + 1, -1));
return maximizeAnd(0, 0, A, B, N, dp);
}
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.io.*;
import java.lang.*;
import java.util.*;
public class GFG {
static int maximizeAnd( int i, int mask, int A[],
int B[], int N, int [][] dp)
{
if (i == N)
return 0 ;
if (dp[i][mask] != - 1 )
return dp[i][mask];
for ( int j = 0 ; j < N; ++j) {
if ((mask & ( 1 << j)) == 0 ) {
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];
}
static int maximizeAndUtil( int A[], int B[], int N)
{
int dp[][] = new int [N][( 1 << N) + 1 ];
for ( int dd[] : dp)
Arrays.fill(dd, - 1 );
return maximizeAnd( 0 , 0 , A, B, N, dp);
}
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));
}
}
|
Python3
def maximizeAnd(i, mask, A, B, N, dp):
if (i = = N):
return 0
if (dp[i][mask] ! = - 1 ):
return dp[i][mask]
for j in range (N):
if ((mask & ( 1 << j)) = = 0 ):
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]
def maximizeAndUtil(A, B, N):
temp = [ - 1 for i in range ( 1 << N + 1 )]
dp = [temp for i in range (N)]
return maximizeAnd( 0 , 0 , A, B, N, dp)
if __name__ = = '__main__' :
A = [ 3 , 5 , 7 , 11 ]
B = [ 2 , 6 , 10 , 12 ]
N = len (A)
print (maximizeAndUtil(A, B, N))
|
C#
using System;
class GFG {
static int maximizeAnd( int i, int mask, int [] A,
int [] B, int N, int [,] dp)
{
if (i == N)
return 0;
if (dp[i, mask] != -1)
return dp[i, mask];
for ( int j = 0; j < N; ++j) {
if ((mask & (1 << j)) == 0) {
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];
}
static int maximizeAndUtil( int [] A, int [] B, int N)
{
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;
}
}
return maximizeAnd(0, 0, A, B, N, dp);
}
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));
}
}
|
Javascript
<script>
function maximizeAnd(i, mask, A, B, N, dp)
{
if (i == N)
return 0;
if (dp[i][mask] != -1)
return dp[i][mask];
for ( var j = 0; j < N; ++j)
{
if (!(mask & (1 << j)))
{
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];
}
function maximizeAndUtil(A, B, N)
{
var dp = Array.from(
Array(N), () => Array(1 << N + 1).fill(-1));
return maximizeAnd(0, 0, A, B, N, dp);
}
var A = [ 3, 5, 7, 11 ];
var B = [ 2, 6, 10, 12 ];
var N = A.length
document.write(maximizeAndUtil(A, B, N));
</script>
|
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;
int maximizeAndUtil( int * A, int * B, int N)
{
vector<vector< int > > dp(N + 1, vector< int >(1 << N, 0));
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)) {
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)]);
}
}
}
}
}
return dp[0][(1 << N) - 1];
}
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.*;
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
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)
{
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()
{
int [] A = { 3, 5, 7, 11 };
int [] B = { 2, 6, 10, 12 };
int N = A.Length;
Console.WriteLine(MaximizeAndUtil(A, B, N));
}
}
|
Javascript
function maximizeAndUtil(A, B, N) {
const dp = new Array(N + 1).fill(0).map(() => new Array(1 << N).fill(0));
for (let mask = 0; mask < (1 << N); mask++) {
dp[N][mask] = 0;
}
for (let i = N - 1; i >= 0; i--) {
for (let j = 0; j < N; j++) {
if ((1 << j) & ((1 << N) - 1)) {
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)]
);
}
}
}
}
}
return dp[0][(1 << N) - 1];
}
const A = [3, 5, 7, 11];
const B = [2, 6, 10, 12];
const N = A.length;
console.log(maximizeAndUtil(A, B, N));
|
Time Complexity: O (N2 * 2N)
Auxiliary Space: O(N * 2N)
Share your thoughts in the comments
Please Login to comment...