Open In App

Rearrange an array to maximize sum of Bitwise AND of same-indexed elements with another array

Last Updated : 19 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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>


Output

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));


Output

22



Time Complexity: O (N2 * 2N)
Auxiliary Space: O(N * 2N



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads