Partition the array in K segments such that bitwise AND of individual segment sum is maximized

Given an array of size N and an integer K. The task is to partition the array in K segments such that bitwise AND of individual segment sum is maximized. Find the maximum value of bitwise AND which can be obtained.

Examples:



Input : a[] = { 2, 5, 6, 9, 1, 3, 10, 12 }, K = 3
Output : 8
Explanation :
Maximum bitwise AND can be obtained by making cut at 3rd element and 5th element(1 based indexing)
(2+5+6)&(9+1)&(3+10+12) = 8

Input : a[] = { 1, 2, 7, 10, 23, 21, 6, 8, 7, 3 } , K = 2
Output : 41
Explanation:
Maximum bitwise AND can be obtained by making cut at 5th element(1 based indexing)
(1+2+7+10+23)&(21+6+8+7+3) = 41

Approach:
First, try to answer a simpler question: given an integer x and determine if it is possible to partition the given array into K segments such that the bitwise AND of sum of segments has all set bits of x?
Let’s denote the sum of elements in the ith segment by S\textsubscript{i}. Also, let’s call a segment good if S\textsubscript{i} has all set bits of x. It’s clear now that for a good segment i, AND(S\textsubscript{i}, x)=x.

Also, all K segments should be good for getting bitwise AND of x. Now to check whether we can partition the array into k good segments. We can use dynamic programming here.

Let dp[i][j]= true, denote that it is possible to partition first i elements into j segments such that all j are good segments otherwise false.

The recurrence for above dp is:

dp[i][j] is 1, if for some index k<i, AND(sum of a[k+1]…a[i], x)=x and dp[k][j-1]=1. Otherwise, dp[i][j] is 0

Build the dp table starting from the most significant bit of the possible answer greedily.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// CPP program to find maximum possible AND
#include <bits/stdc++.h>
using namespace std;
  
// Function to check whether a k segment partition 
// is possible such that bitwise AND is 'mask'
bool checkpossible(int mask, int* arr, int* prefix, int n, 
                                                  int k)
{
    // dp[i][j] stores whether it is possible to partition
    // first i elements into j segments such that all j 
    // segments are 'good'
    bool dp[n + 1][k + 1];
  
    // Initialising dp
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
  
    // Filling dp in bottom-up manner
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            // Finding a cut such that first l elements 
            // can be partitioned into j-1 'good' segments
            // and arr[l+1]+...+arr[i] is a 'good' segment
            for (int l = i - 1; l >= 0; --l) {
                if (dp[l][j - 1] && (((prefix[i] - prefix[l]) 
                           & mask) == mask)) {
                    dp[i][j] = 1;
                    break;
                }
            }
        }
    }
  
    return dp[n][k];
}
  
// Function to find maximum possible AND
int Partition(int arr[], int n, int k)
{
    // Array to store prfix sums
    int prefix[n+1];
  
    for (int i = 1; i <= n; i++) {
        prefix[i] = prefix[i - 1] + arr[i];
    }
  
    // Maximum no of bits in the possible answer
    int LOGS = 20;
  
    // This will store the final answer
    int ans = 0;
  
    // Constructing answer greedily selecting 
    // from the higher most bit
    for (int i = LOGS; i >= 0; --i) {
        // Checking if array can be partitioned 
        // such that the bitwise AND is ans|(1<<i)
        if (checkpossible(ans | (1 << i), arr, prefix, n, k))
        {
            // if possible, update the answer
            ans = ans | (1 << i);
        }
    }
  
    // Return the final answer
    return ans;
}
  
// Driver code
int main()
{
      
    int arr[]={0, 1, 2, 7, 10, 23, 21, 6, 8, 7, 3}, k = 2;
  
    // n = 11 , first element is zero
    // to make array 1 based indexing. So, number of 
    // elements are 10 
    int n = sizeof(arr)/sizeof(arr[0])-1;
      
    // Function call
    cout << Partition(arr, n, k);
      
    return 0;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java program to find maximum possible AND 
  
class GFG 
{
      
    // Function to check whether a k segment partition 
    // is possible such that bitwise AND is 'mask' 
    static boolean checkpossible(int mask, int arr[], 
                                int prefix[], int n, int k) 
    
        int i,j;
          
        // dp[i][j] stores whether it is possible to partition 
        // first i elements into j segments such that all j 
        // segments are 'good' 
        boolean dp[][] = new boolean[n + 1][k + 1]; 
      
        // Initialising dp 
        for(i = 0; i < n + 1; i++)
        {
            for(j = 0; j < k + 1; j++)
            {
                dp[i][j] = false ;
            }
        }
          
        dp[0][0] = true
      
        // Filling dp in bottom-up manner 
        for ( i = 1; i <= n; i++) 
        
            for (j = 1; j <= k; j++)
            
                // Finding a cut such that first l elements 
                // can be partitioned into j-1 'good' segments 
                // and arr[l+1]+...+arr[i] is a 'good' segment 
                for (int l = i - 1; l >= 0; --l)
                
                    if (dp[l][j - 1] && (((prefix[i] - prefix[l]) 
                            & mask) == mask))
                    
                        dp[i][j] = true
                        break
                    
                
            
        
      
        return dp[n][k]; 
    
      
    // Function to find maximum possible AND 
    static int Partition(int arr[], int n, int k) 
    
        // Array to store prfix sums 
        int prefix[] = new int[n+1]; 
      
        for (int i = 1; i <= n; i++)
        
            prefix[i] = prefix[i - 1] + arr[i]; 
        
      
        // Maximum no of bits in the possible answer 
        int LOGS = 20
      
        // This will store the final answer 
        int ans = 0
      
        // Constructing answer greedily selecting 
        // from the higher most bit 
        for (int i = LOGS; i >= 0; --i) 
        
            // Checking if array can be partitioned 
            // such that the bitwise AND is ans|(1<<i) 
            if (checkpossible(ans | (1 << i), arr, prefix, n, k)) 
            
                // if possible, update the answer 
                ans = ans | (1 << i); 
            
        
      
        // Return the final answer 
        return ans; 
    
  
    // Driver code 
    public static void main (String[] args) 
    {
          
        int arr[] = {0, 1, 2, 7, 10, 23, 21, 6, 8, 7, 3}, k = 2
      
        // n = 11 , first element is zero 
        // to make array 1 based indexing. So, number of 
        // elements are 10 
        int n = arr.length - 1
          
        // Function call 
        System.out.println(Partition(arr, n, k)); 
    }
}
  
// This code is contributed by AnkitRai01

chevron_right


Python3

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python3 program to find maximum possible AND
  
# Function to check whether a k segment partition
# is possible such that bitwise AND is 'mask'
def checkpossible(mask,arr,prefix, n,k):
      
    # dp[i][j] stores whether it is possible to partition
    # first i elements into j segments such that all j
    # segments are 'good'
    dp=[[0 for i in range(k+1)] for i in range(n + 1)]
  
    # Initialising dp
    dp[0][0] = 1
  
    # Filling dp in bottom-up manner
    for i in range(1, n+1):
        for j in range(1, k+1):
              
            # Finding a cut such that first l elements
            # can be partitioned into j-1 'good' segments
            # and arr[l+1]+...+arr[i] is a 'good' segment
            for l in range(i-1,-1,-1):
                if (dp[l][j - 1] and (((prefix[i] - prefix[l]) & mask) == mask)):
                    dp[i][j] = 1
                    break
  
    return dp[n][k]
  
  
# Function to find maximum possible AND
def Partition(arr, n, k):
    # Array to store prfix sums
    prefix=[0 for i in range(n+1)]
  
    for i in range(1,n+1):
        prefix[i] = prefix[i - 1] + arr[i]
  
    # Maximum no of bits in the possible answer
    LOGS = 20
  
    # This will store the final answer
    ans = 0
  
    # Constructing answer greedily selecting
    # from the higher most bit
    for i in range(LOGS,-1,-1):
        # Checking if array can be partitioned
        # such that the bitwise AND is ans|(1<<i)
        if (checkpossible(ans | (1 << i), arr, prefix, n, k)):
            # if possible, update the answer
            ans = ans | (1 << i)
  
  
    # Return the final answer
    return ans
  
  
# Driver code
  
arr = [0, 1, 2, 7, 10, 23, 21, 6, 8, 7, 3]
k = 2
  
# n = 11 , first element is zero
# to make array 1 based indexing. So, number of
# elements are 10
n = len(arr)-1
  
# Function call
print(Partition(arr, n, k))
  
# This code is contributed by mohit kumar 29

chevron_right


C#

filter_none

edit
close

play_arrow

link
brightness_4
code

// C# program to find maximum possible AND 
using System;
      
class GFG 
{
      
    // Function to check whether a 
    // k-segment partition is possible
    // such that bitwise AND is 'mask' 
    static Boolean checkpossible(int mask, int []arr, 
                                 int []prefix, 
                                 int n, int k) 
    
        int i, j;
          
        // dp[i,j] stores whether it is possible 
        // to partition first i elements into 
        // j-segments such that all j-segments are 'good' 
        Boolean[,] dp = new Boolean[n + 1, k + 1]; 
      
        // Initialising dp 
        for(i = 0; i < n + 1; i++)
        {
            for(j = 0; j < k + 1; j++)
            {
                dp[i, j] = false;
            }
        }
          
        dp[0, 0] = true
      
        // Filling dp in bottom-up manner 
        for ( i = 1; i <= n; i++) 
        
            for (j = 1; j <= k; j++)
            
                // Finding a cut such that first l elements 
                // can be partitioned into j-1 'good' segments 
                // and arr[l+1]+...+arr[i] is a 'good' segment 
                for (int l = i - 1; l >= 0; --l)
                
                    if (dp[l, j - 1] && 
                     (((prefix[i] - prefix[l]) & 
                        mask) == mask))
                    
                        dp[i, j] = true
                        break
                    
                
            
        
        return dp[n, k]; 
    
      
    // Function to find maximum possible AND 
    static int Partition(int []arr, int n, int k) 
    
        // Array to store prfix sums 
        int []prefix = new int[n + 1]; 
      
        for (int i = 1; i <= n; i++)
        
            prefix[i] = prefix[i - 1] + arr[i]; 
        
      
        // Maximum no of bits in the possible answer 
        int LOGS = 20; 
      
        // This will store the final answer 
        int ans = 0; 
      
        // Constructing answer greedily selecting 
        // from the higher most bit 
        for (int i = LOGS; i >= 0; --i) 
        
            // Checking if array can be partitioned 
            // such that the bitwise AND is ans|(1<<i) 
            if (checkpossible(ans | (1 << i), 
                          arr, prefix, n, k)) 
            
                // if possible, update the answer 
                ans = ans | (1 << i); 
            
        
      
        // Return the final answer 
        return ans; 
    
  
    // Driver code 
    public static void Main (String[] args) 
    {
          
        int []arr = {0, 1, 2, 7, 10, 23,
                         21, 6, 8, 7, 3};
        int k = 2; 
      
        // n = 11 , first element is zero 
        // to make array 1 based indexing. 
        // So, number of elements are 10 
        int n = arr.Length - 1 ; 
          
        // Function call 
        Console.WriteLine(Partition(arr, n, k)); 
    }
}
  
// This code is contributed by 29AjayKumar

chevron_right


Output:

41

Time Complexity : O(n^2klog(S)), where S = S\textsubscript{1}+S\textsubscript{2}+….+S\textsubscript{k}.



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.