Optimally accommodate 0s and 1s from a Binary String into K buckets

Given a binary string S, consisting of 0’s and 1’s. You have to accommodate the 0’s and 1’s into the K buckets in such a way that the following conditions are satisfied:

  1. You fill the 0’s and 1’s into the buckets preserving the relative order of 0’s and 1’s. For example, you cannot put S[1] into bucket 2 and S[0] into bucket 1. You have to preserve the original ordering of the binary string.
  2. No bucket should be left empty and no element in the string should be left unaccommodated.
  3. The sum of all the products of (number of 0’s * number of 1’s) for each bucket should be the minimum among all possible accommodation arrangements.

If a solution is not possible, then print -1.

Examples:



Input: S = "0001", K = 2
Output: 0
We have 3 choices {"0", "001"}, {"00", "01"}, {"000", 1}
First choice, we will get 1*0 + 2*1 = 2
Second choice, we will get 2*0 + 1*1 = 1
Third choice, we will get 3*0 + 0*1 = 0
Out of all the 3 choices, the third choice 
is giving the minimum answer.

Input: S = "0101", K = 1
Output: 1

Recursive implementation: You have to accomodate binary string into K buckets without disturbing the above conditions. Then simple recursive solution can be made by first filling up i-th bucket (starting from 0) by putting elements from start to N (N = length of binary string) and keep adding count zeroes and ones till start index. For each iteration, if there x zeroes and y ones till start then recur for f(start, K) = x * y + f(start + 1, K – 1) because next accomodation will be made from (start + 1)-th index and remaining buckets will K – 1.
So, the recursive formula will be –

F(start, current_bucket) =  |           |
                            |       min |  F(i + 1, next_bucket) + (ones * zeroes in current_bucket)  
                            |           |   
                            | i = start to N

Top-Down Dynamic Approach:
The recursive relation can be changed to Dynamic Solution by saving the results of different combinations of start and bucket variable into 2-D DP array. We can use the fact that the order of the string should be preserved. You can have a 2-D array saving the states of size [size of string * buckets], where dp[i][j] will tell us minimum value of accommodation till i’th index of the string using j + 1 buckets. Our final answer will be in dp[N-1][K-1].

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
  
// 2-D dp array saving different states
// dp[i][j] = minimum value of accommodation
// till i'th index of the string
// using j+1 number of buckets.
vector<vector<int> > dp;
  
// Function to find the minimum required
// sum using dynamic programming
int solveUtil(int start, int bucket, string str, int K)
{
    int N = str.size();
  
    // If both start and bucket reached end then
    // return 0 else that arrangement is not possible
    // so return INT_MAX
    if (start == N) {
        if (bucket == K)
            return 0;
        return INT_MAX;
    }
  
    // Corner case
    if (bucket == K)
        return INT_MAX;
  
    // If state if already calculated
    // then return its answer
    if (dp[start][bucket] != -1)
        return dp[start][bucket];
  
    int zeroes = 0;
    int ones = 0;
    int ans = INT_MAX;
  
    // Start filling zeroes and ones which to be accommodated
    // in jth bucket then ans for current arrangement will be
    // ones*zeroes + recur(i+1, buket+1)
    for (int i = start; i < N; ++i) {
        if (str[i] == '1')
            ones++;
        else
            zeroes++;
  
        if (ones * zeroes > ans)
            break;
  
        int temp = solveUtil(i + 1, bucket + 1, str, K);
  
        // If this arrangement is not possible then
        // don't calculate further
        if (temp != INT_MAX) {
            ans = min(ans, temp + (ones * zeroes));
        }
    }
  
    return dp[start][bucket] = ans;
}
  
// Function to initialze the dp and call
// solveUtil() method to get the answer
int solve(string str, int K)
{
    int N = str.size();
    dp.clear();
    dp.resize(N, vector<int>(K, -1));
  
    // Start with 0-th index and 1 bucket
    int ans = solveUtil(0, 0, str, K);
  
    return ans == INT_MAX ? -1 : ans;
}
  
// Driver code
int main()
{
    string S = "0101";
  
    // K buckets
    int K = 2;
  
    cout << solve(S, K) << endl;
  
    return 0;
}

chevron_right


Python3

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python3 implementation of the approach
  
# 2-D dp array saving different states
# dp[i][j] = minimum value of accommodation
# till i'th index of the str1ing
# using j+1 number of buckets.
  
# Function to find the minimum required
# sum using dynamic programming
def solveUtil(start, bucket, str1, K,dp):
  
    N = len(str1)
  
    # If both start and bucket reached end then
    # return 0 else that arrangement is not possible
    # so return INT_MAX
    if (start == N) :
        if (bucket == K):
            return 0
        return 10**9
  
  
    # Corner case
    if (bucket == K):
        return 10**9
  
    # If state if already calculated
    # then return its answer
    if (dp[start][bucket] != -1):
        return dp[start][bucket]
  
    zeroes = 0
    ones = 0
    ans = 10**9
  
    # Start filling zeroes and ones which to be accommodated
    # in jth bucket then ans for current arrangement will be
    # ones*zeroes + recur(i+1, buket+1)
    for i in range(start,N):
        if (str1[i] == '1'):
            ones += 1
        else:
            zeroes += 1
  
        if (ones * zeroes > ans):
            break
  
        temp = solveUtil(i + 1, bucket + 1, str1, K,dp)
  
        # If this arrangement is not possible then
        # don't calculate further
        if (temp != 10**9):
            ans = min(ans, temp + (ones * zeroes))
  
    dp[start][bucket] = ans
  
    return ans
  
  
# Function to initialze the dp and call
# solveUtil() method to get the answer
def solve(str1, K):
  
    N = len(str1)
  
    dp = [[-1 for i in range(K)] for i in range(N)]
  
    # Start with 0-th index and 1 bucket
    ans = solveUtil(0, 0, str1, K,dp)
  
    if ans == 10**9:
        return -1
    else:
        return ans
  
  
# Driver code
  
s = "0101"
S=[i for i in s]
  
# K buckets
K = 2
  
print(solve(S, K))
  
# This code is contributed by mohit kumar 29
  
  
  

chevron_right


Output:

2

Time Complexity: O(N3)
Space Complexity: O(N2)

This solution is still not optimised because it calls the same state a number of times. So, now look into the Optimised Bottom Up DP Approach.

Bottom-Up Dynamic Approach: Let us try to think about the final state first. Here the variables are the number of buckets and the index of the string. Let dp[i][j] be the minimum sum of products for string elements 0 to j-1 and i buckets. Now to define our transition function, we will have to start from the back and consider partition at each possible position k. Hence our transition function looks like:

dp [i][j] = for all k = 0 to j min(dp[i][k-1] + numberOfZeroes * numberOfOnes)

for i = 0 (single partition) simple count number of 0's and 1's and do the multiplication. 
And if number of buckets is more than string length till now ans is -1 as we cant fill all 
the available buckets.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
  
// Function to find the minimum required
// sum using dynamic programming
int solve(string str, int K)
{
    int n = str.length();
  
    // dp[i][j] = minimum val of accommodation
    // till j'th index of the string
    // using i+1 number of buckets.
    // Final ans will be in dp[n-1][K-1]
    long long dp[K][n];
  
    // Initialise dp with all states as 0
    memset(dp, 0, sizeof dp);
  
    // Corner cases
    if (n < K)
        return -1;
    else if (n == K)
        return 0;
  
    // Filling first row, if only 1 bucket then simple count
    // number of zeros and ones and do the multiplication
    long long zeroes = 0, ones = 0;
  
    for (int i = 0; i < n; i++) {
        if (str[i] == '0')
            zeroes++;
        else
            ones++;
  
        dp[0][i] = ones * zeroes;
    }
  
    for (int s = 1; s < K; s++) {
        for (int i = 0; i < n; i++) {
  
            dp[s][i] = INT_MAX;
  
            ones = 0;
            zeroes = 0;
            for (int k = i; k >= 0; k--) {
                if (str[k] == '0')
                    zeroes++;
                else
                    ones++;
  
                // If k = 0 then this arrangement is not possible
                dp[s][i] = min(dp[s][i],
                               +((k - 1 >= 0)
                                     ? ones * zeroes + dp[s - 1][k - 1]
                                     : INT_MAX));
            }
        }
    }
  
    // If no arrangement is possible then
    // our answer will remain INT_MAX so return -1
    return (dp[K - 1][n - 1] == INT_MAX) ? -1 : dp[K - 1][n - 1];
}
  
// Driver code
int main()
{
    string S = "0101";
  
    // K buckets
    int K = 2;
  
    cout << solve(S, K) << endl;
  
    return 0;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java implementation of the approach
  
class GFG
{
    // Function to find the minimum required
    // sum using dynamic programming
    static long solve(String str, int K)
    {
        int n = str.length();
      
        // dp[i][j] = minimum val of accommodation
        // till j'th index of the string
        // using i+1 number of buckets.
        // Final ans will be in dp[n-1][K-1]
        long dp[][] = new long[K][n];
      
      
        // Corner cases
        if (n < K)
            return -1;
        else if (n == K)
            return 0;
      
        // Filling first row, if only 1 bucket then simple count
        // number of zeros and ones and do the multiplication
        long zeroes = 0, ones = 0;
      
        for (int i = 0; i < n; i++) 
        {
            if (str.charAt(i) == '0')
                zeroes++;
            else
                ones++;
      
            dp[0][i] = ones * zeroes;
        }
      
        for (int s = 1; s < K; s++)
        {
            for (int i = 0; i < n; i++) 
            {
      
                dp[s][i] = Integer.MAX_VALUE;
      
                ones = 0;
                zeroes = 0;
                for (int k = i; k >= 0; k--) 
                {
                    if (str.charAt(k) == '0')
                        zeroes++;
                    else
                        ones++;
      
                    // If k = 0 then this arrangement is not possible
                    dp[s][i] = Math.min(dp[s][i],
                                +((k - 1 >= 0)
                                        ? ones * zeroes + dp[s - 1][k - 1]
                                        : Integer.MAX_VALUE));
                }
            }
        }
      
        // If no arrangement is possible then
        // our answer will remain INT_MAX so return -1
        return (dp[K - 1][n - 1] == Integer.MAX_VALUE) ? -1 : dp[K - 1][n - 1];
    }
      
    // Driver code
    public static void main (String[] args)
    {
      
        String S = "0101";
      
        // K buckets
        int K = 2;
      
        System.out.println(solve(S, K));
    }
}
  
// This code is contributed by ihritik

chevron_right


C#

filter_none

edit
close

play_arrow

link
brightness_4
code

// C# implementation of the approach
using System;
  
class GFG
{
    // Function to find the minimum required
    // sum using dynamic programming
    static long solve(string str, int K)
    {
        int n = str.Length;
      
        // dp[i, j] = minimum val of accommodation
        // till j'th index of the string
        // using i+1 number of buckets.
        // Final ans will be in dp[n-1, K-1]
        long [, ] dp = new long[K, n];
      
      
        // Corner cases
        if (n < K)
            return -1;
        else if (n == K)
            return 0;
      
        // Filling first row, if only 1 bucket then simple count
        // number of zeros and ones and do the multiplication
        long zeroes = 0, ones = 0;
      
        for (int i = 0; i < n; i++) 
        {
            if (str[i] == '0')
                zeroes++;
            else
                ones++;
      
            dp[0, i] = ones * zeroes;
        }
      
        for (int s = 1; s < K; s++)
        {
            for (int i = 0; i < n; i++)
            {
      
                dp[s, i] = Int32.MaxValue;
      
                ones = 0;
                zeroes = 0;
                for (int k = i; k >= 0; k--)
                {
                    if (str[k] == '0')
                        zeroes++;
                    else
                        ones++;
      
                    // If k = 0 then this arrangement is not possible
                    dp[s, i] = Math.Min(dp[s, i],
                                +((k - 1 >= 0)
                                        ? ones * zeroes + dp[s - 1, k - 1]
                                        : Int32.MaxValue));
                }
            }
        }
      
        // If no arrangement is possible then
        // our answer will remain INT_MAX so return -1
        return (dp[K - 1, n - 1] == Int32.MaxValue) ? -1 : dp[K - 1, n - 1];
    }
      
    // Driver code
    public static void Main (string[] args)
    {
      
        string S = "0101";
      
        // K buckets
        int K = 2;
      
        Console.WriteLine(solve(S, K));
      
    }
          
}
  
// This code is contributed by ihritik

chevron_right


Output:

2

Time Complexity: O(N3)
Space Complexity: O(N2)



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.



Improved By : ihritik, mohit kumar 29