Open In App

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 :
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 . Also, let’s call a segment good if has all set bits of x. It’s clear now that for a good segment i, AND(, 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: 
 

// 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 prefix 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;
}

                    
// 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 prefix 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

                    
# 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 prefix 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

                    
// 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 prefix 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

                    
<script>
    // Javascript program to find maximum possible AND
     
    // Function to check whether a k segment partition
    // is possible such that bitwise AND is 'mask'
    function checkpossible(mask, arr, prefix, n, k)
    {
        let 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'
        let dp = new Array(n + 1);
       
        // Initialising dp
        for(i = 0; i < n + 1; i++)
        {
            dp[i] = new Array(k + 1);
            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 (let 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
    function Partition(arr, n, k)
    {
        // Array to store prefix sums
        let prefix = new Array(n+1);
        prefix.fill(0);
       
        for (let i = 1; i <= n; i++)
        {
            prefix[i] = prefix[i - 1] + arr[i];
        }
       
        // Maximum no of bits in the possible answer
        let LOGS = 20;
       
        // This will store the final answer
        let ans = 0;
       
        // Constructing answer greedily selecting
        // from the higher most bit
        for (let 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;
    }
     
    let 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
    let n = arr.length - 1 ;
 
    // Function call
    document.write(Partition(arr, n, k));
 
// This code is contributed by divyeshrabadiya07.
</script>

                    

Output: 
41

 

.
 Performance Analysis:

Time Complexity: O(20*n2k) in the worst case.

Space Complexity: O(n*k) in worst case.


Article Tags :