The painter’s partition problem

03 Jun, 2022
We have to paint n boards of length {A1, A2...An}. There are k painters available and each takes 1 unit time to paint 1 unit of board. The problem is to find the minimum time to get this job done under the constraints that any painter will only paint continuous sections of boards, say board {2, 3, 4} or only board {1} or nothing but not board {2, 4, 5}.
Examples: 
Input : k = 2, A = {10, 10, 10, 10} 
Output : 20.
Here we can divide the boards into 2
equal sized partitions, so each painter 
gets 20 units of board and the total
time taken is 20. 

Input : k = 2, A = {10, 20, 30, 40} 
Output : 60.
Here we can divide first 3 boards for
one painter and the last board for 
second painter.
From the above examples, it is obvious that the strategy of dividing the boards into k equal partitions won’t work for all the cases. We can observe that the problem can be broken down into: Given an array A of non-negative integers and a positive integer k, we have to divide A into k of fewer partitions such that the maximum sum of the elements in a partition, overall partitions is minimized. So for the second example above, possible divisions are: 
* One partition: so time is 100. 
* Two partitions: (10) & (20, 30, 40), so time is 90. Similarly we can put the first divider after 20 (=> time 70) or 30 (=> time 60); so this means the minimum time: (100, 90, 70, 60) is 60. A brute force solution is to consider all possible set of contiguous partitions and calculate the maximum sum partition in each case and return the minimum of all these cases. 
1) Optimal Substructure: We can implement the naive solution using recursion with the following optimal substructure property: Assuming that we already have k-1 partitions in place (using k-2 dividers), we now have to put the k-1 th divider to get k partitions. How can we do this? We can put the k-1 th divider between the i th and i+1 th element where i = 1 to n. Please note that putting it before the first element is the same as putting it after the last element.The total cost of this arrangement can be calculated as the maximum of the following: a) The cost of the last partition: sum(Ai..An), where the k-1 th divider is before element i. b) The maximum cost of any partition already formed to the left of the k-1 th divider.Here a) can be found out using a simple helper function to calculate sum of elements between two indices in the array. How to find out b) ? We can observe that b) actually is to place the k-2 separators as fairly as possible, so it is a subproblem of the given problem. Thus we can write the optimal substructure property as the following recurrence relation: 
Following is the implementation of the above recursive equation: 
C++
Java
Python3
C#
PHP
Javascript
// CPP program for The painter's partition problem#include <climits>#include <iostream>using namespace std; // function to calculate sum between two indices// in arrayint sum(int arr[], int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;} // for n boards and k partitionsint partition(int arr[], int n, int k){    // base cases       if (k == 1) // one partition        return sum(arr, 0, n - 1);       if (n == 1)  // one board        return arr[0];     int best = INT_MAX;     // find minimum of all possible maximum    // k-1 partitions to the left of arr[i],    // with i elements, put k-1 th divider    // between arr[i-1] & arr[i] to get k-th    // partition    for (int i = 1; i <= n; i++)        best = min(best, max(partition(arr, i, k - 1),                                sum(arr, i, n - 1)));     return best;} int main(){    int arr[] = { 10, 20, 60, 50, 30, 40 };    int n = sizeof(arr) / sizeof(arr[0]);    int k = 3;    cout << partition(arr, n, k) << endl;     return 0;}
// Java Program for The painter's partition problemimport java.util.*;import java.io.*; class GFG{// function to calculate sum between two indices// in arraystatic int sum(int arr[], int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;}  // for n boards and k partitionsstatic int partition(int arr[], int n, int k){    // base cases       if (k == 1) // one partition        return sum(arr, 0, n - 1);       if (n == 1)  // one board        return arr[0];      int best = Integer.MAX_VALUE;      // find minimum of all possible maximum    // k-1 partitions to the left of arr[i],    // with i elements, put k-1 th divider    // between arr[i-1] & arr[i] to get k-th    // partition    for (int i = 1; i <= n; i++)        best = Math.min(best, Math.max(partition(arr, i, k - 1),                                sum(arr, i, n - 1)));      return best;} // Driver codepublic static void main(String args[]){ int arr[] = { 10, 20, 60, 50, 30, 40 };      // Calculate size of array.    int n = arr.length;        int k = 3; System.out.println(partition(arr, n, k));}} // This code is contributed by Sahil_Bansall
# Python program for The painter's# partition problem function to# calculate sum between two indices# in arraydef sum(arr, frm, to):    total = 0;    for i in range(frm, to + 1):        total += arr[i]    return total     # for n boards and k partitionsdef partition(arr, n, k):         # base cases    if k == 1: # one partition        return sum(arr, 0, n - 1)    if n == 1: # one board        return arr[0]    best = 100000000         # find minimum of all possible     # maximum k-1 partitions to     # the left of arr[i], with i    # elements, put k-1 th divider    # between arr[i-1] & arr[i] to    # get k-th partition    for i in range(1, n + 1):        best = min(best,               max(partition(arr, i, k - 1),                         sum(arr, i, n - 1)))    return best     # Driver Codearr = [10, 20, 60, 50, 30, 40 ]n = len(arr)k = 3print(partition(arr, n, k)) # This code is contributed# by sahilshelangia
// C# Program for The painter's partition problemusing System; class GFG {     // function to calculate sum// between two indices in arraystatic int sum(int []arr, int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;} // for n boards and k partitionsstatic int partition(int []arr, int n, int k){    // base cases    if (k == 1) // one partition        return sum(arr, 0, n - 1);             if (n == 1) // one board        return arr[0];     int best = int.MaxValue;     // find minimum of all possible maximum    // k-1 partitions to the left of arr[i],    // with i elements, put k-1 th divider    // between arr[i-1] & arr[i] to get k-th    // partition    for (int i = 1; i <= n; i++)        best = Math.Min(best, Math.Max(partition(arr, i, k - 1),                                           sum(arr, i, n - 1)));     return best;} // Driver codepublic static void Main(){    int []arr = {10, 20, 60, 50, 30, 40};     // Calculate size of array.    int n = arr.Length;    int k = 3;         // Function calling    Console.WriteLine(partition(arr, n, k));}} // This code is contributed by vt_m
<?php// PHP program for The// painter's partition problem // function to calculate sum// between two indices in arrayfunction sum($arr, $from, $to){    $total = 0;    for ($i = $from; $i <= $to; $i++)        $total += $arr[$i];    return $total;} // for n boards// and k partitionsfunction partition($arr, $n, $k){    // base cases    if ($k == 1) // one partition        return sum($arr, 0, $n - 1);    if ($n == 1) // one board        return $arr[0];     $best = PHP_INT_MAX;     // find minimum of all possible    // maximum k-1 partitions to the    // left of arr[i], with i elements,    // put k-1 th divider between    // arr[i-1] & arr[i] to get k-th    // partition    for ($i = 1; $i <= $n; $i++)        $best = min($best,                max(partition($arr, $i, $k - 1),                          sum($arr, $i, $n - 1)));     return $best;}// Driver Code$arr = array(10, 20, 60,             50, 30, 40);$n = sizeof($arr);$k = 3;echo partition($arr, $n, $k), "\n"; // This code is contributed by ajit?>
<script> // JavaScript Program for The painter's// partition problem // Function to calculate sum between// two indices in arrayfunction sum(arr, from, to){    let total = 0;    for(let i = from; i <= to; i++)        total += arr[i];             return total;}   // For n boards and k partitionsfunction partition(arr, n, k){         // Base cases      if (k == 1) // one partition        return sum(arr, 0, n - 1);      if (n == 1)  // one board        return arr[0];       let best = Number.MAX_VALUE;       // Find minimum of all possible maximum    // k-1 partitions to the left of arr[i],    // with i elements, put k-1 th divider    // between arr[i-1] & arr[i] to get k-th    // partition    for(let i = 1; i <= n; i++)        best = Math.min(best,               Math.max(partition(arr, i, k - 1),                              sum(arr, i, n - 1)));       return best;} // Driver Codelet arr = [ 10, 20, 60, 50, 30, 40 ]; // Calculate size of array.let n = arr.length;let k = 3; document.write(partition(arr, n, k)); // This code is contributed by susmitakundugoaldanga </script>
Output : 
90
The time complexity of the above solution is exponential. 
2) Overlapping subproblems: Following is the partial recursion tree for T(4, 3) in above equation. 
      T(4, 3)
     /    /    \ ..         
T(1, 2)  T(2, 2) T(3, 2) 
          /..      /..     
      T(1, 1)    T(1, 1)
We can observe that many subproblems like T(1, 1) in the above problem are being solved again and again. Because of these two properties of this problem, we can solve it using dynamic programming, either by top down memoized method or bottom up tabular method. Following is the bottom up tabular implementation: 
C++
Java
Python3
C#
PHP
Javascript
// A DP based CPP program for painter's partition problem#include <climits>#include <iostream>using namespace std; // function to calculate sum between two indices// in arrayint sum(int arr[], int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;} // bottom up tabular dpint findMax(int arr[], int n, int k){    // initialize table    int dp[k + 1][n + 1] = { 0 };     // base cases    // k=1    for (int i = 1; i <= n; i++)        dp[1][i] = sum(arr, 0, i - 1);     // n=1    for (int i = 1; i <= k; i++)        dp[i][1] = arr[0];     // 2 to k partitions    for (int i = 2; i <= k; i++) { // 2 to n boards        for (int j = 2; j <= n; j++) {             // track minimum            int best = INT_MAX;             // i-1 th separator before position arr[p=1..j]            for (int p = 1; p <= j; p++)                best = min(best, max(dp[i - 1][p],                              sum(arr, p, j - 1)));                   dp[i][j] = best;        }    }     // required    return dp[k][n];} // driver functionint main(){    int arr[] = { 10, 20, 60, 50, 30, 40 };    int n = sizeof(arr) / sizeof(arr[0]);    int k = 3;    cout << findMax(arr, n, k) << endl;    return 0;}
// A DP based Java program for// painter's partition problemimport java.util.*;import java.io.*; class GFG{// function to calculate sum between two indices// in arraystatic int sum(int arr[], int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;}  // bottom up tabular dpstatic int findMax(int arr[], int n, int k){    // initialize table    int dp[][] = new int[k+1][n+1];      // base cases    // k=1    for (int i = 1; i <= n; i++)        dp[1][i] = sum(arr, 0, i - 1);      // n=1    for (int i = 1; i <= k; i++)        dp[i][1] = arr[0];      // 2 to k partitions    for (int i = 2; i <= k; i++) { // 2 to n boards        for (int j = 2; j <= n; j++) {              // track minimum            int best = Integer.MAX_VALUE;              // i-1 th separator before position arr[p=1..j]            for (int p = 1; p <= j; p++)                best = Math.min(best, Math.max(dp[i - 1][p],                              sum(arr, p, j - 1)));                    dp[i][j] = best;        }    }      // required    return dp[k][n];} // Driver codepublic static void main(String args[]){ int arr[] = { 10, 20, 60, 50, 30, 40 };      // Calculate size of array.    int n = arr.length;        int k = 3; System.out.println(findMax(arr, n, k));}} // This code is contributed by Sahil_Bansall
# A DP based Python3 program for# painter's partition problem # function to calculate sum between# two indices in listdef sum(arr, start, to):    total = 0    for i in range(start, to + 1):        total += arr[i]    return total # bottom up tabular dpdef findMax(arr, n, k):         # initialize table    dp = [[0 for i in range(n + 1)]             for j in range(k + 1)]     # base cases    # k=1    for i in range(1, n + 1):        dp[1][i] = sum(arr, 0, i - 1)     # n=1    for i in range(1, k + 1):        dp[i][1] = arr[0]     # 2 to k partitions    for i in range(2, k + 1): # 2 to n boards        for j in range(2, n + 1):                         # track minimum            best = 100000000                         # i-1 th separator before position arr[p=1..j]            for p in range(1, j + 1):                best = min(best, max(dp[i - 1][p],                                 sum(arr, p, j - 1)))                 dp[i][j] = best     # required    return dp[k][n] # Driver Codearr = [10, 20, 60, 50, 30, 40]n = len(arr)k = 3print(findMax(arr, n, k)) # This code is contributed by ashutosh450
// A DP based C# program for// painter's partition problemusing System; class GFG {     // function to calculate sum between// two indices in arraystatic int sum(int []arr, int from, int to){    int total = 0;    for (int i = from; i <= to; i++)        total += arr[i];    return total;} // bottom up tabular dpstatic int findMax(int []arr, int n, int k){    // initialize table    int [,]dp = new int[k+1,n+1];     // base cases    // k=1    for (int i = 1; i <= n; i++)        dp[1,i] = sum(arr, 0, i - 1);     // n=1    for (int i = 1; i <= k; i++)        dp[i,1] = arr[0];     // 2 to k partitions    for (int i = 2; i <= k; i++) { // 2 to n boards        for (int j = 2; j <= n; j++) {             // track minimum            int best = int.MaxValue;             // i-1 th separator before position arr[p=1..j]            for (int p = 1; p <= j; p++)                best = Math.Min(best, Math.Max(dp[i - 1,p],                                      sum(arr, p, j - 1)));             dp[i,j] = best;        }    }     // required    return dp[k,n];} // Driver codepublic static void Main(){    int []arr = {10, 20, 60, 50, 30, 40};     // Calculate size of array.    int n = arr.Length;    int k = 3;    Console.WriteLine(findMax(arr, n, k));}} // This code is contributed by vt_m
<?php// A DP based PHP program for// painter's partition problem // function to calculate sum// between two indices in arrayfunction sum($arr, $from, $to){    $total = 0;    for ($i = $from; $i <= $to; $i++)        $total += $arr[$i];    return $total;} // bottom up tabular dpfunction findMax($arr, $n, $k){    // initialize table    $dp[$k + 1][$n + 1] = array( 0 );     // base cases    // k=1    for ($i = 1; $i <= $n; $i++)        $dp[1][$i] = sum($arr, 0,                         $i - 1);     // n=1    for ($i = 1; $i <= $k; $i++)        $dp[$i][1] = $arr[0];     // 2 to k partitions    for ($i = 2; $i <= $k; $i++)    {        // 2 to n boards        for ($j = 2; $j <= $n; $j++)        {             // track minimum            $best = PHP_INT_MAX;             // i-1 th separator before            // position arr[p=1..j]            for ($p = 1; $p <= $j; $p++)                $best = min($best, max($dp[$i - 1][$p],                               sum($arr, $p, $j - 1)));                 $dp[$i][$j] = $best;        }    }     // required    return $dp[$k][$n];} // Driver Code$arr = array (10, 20, 60,              50, 30, 40 );$n = sizeof($arr);$k = 3;echo findMax($arr, $n, $k) ,"\n"; // This code is contributed by m_kit?>
<script>    // A DP based Javascript program for    // painter's partition problem         // function to calculate sum between    // two indices in array    function sum(arr, from, to)    {        let total = 0;        for (let i = from; i <= to; i++)            total += arr[i];        return total;    }         // bottom up tabular dp    function findMax(arr, n, k)    {        // initialize table        let dp = new Array(k+1);        for(let i = 0; i < k + 1; i++)        {            dp[i] = new Array(n + 1);        }         // base cases        // k=1        for (let i = 1; i <= n; i++)            dp[1][i] = sum(arr, 0, i - 1);         // n=1        for (let i = 1; i <= k; i++)            dp[i][1] = arr[0];         // 2 to k partitions        for (let i = 2; i <= k; i++) { // 2 to n boards            for (let j = 2; j <= n; j++) {                 // track minimum                let best = Number.MAX_VALUE;                 // i-1 th separator before position arr[p=1..j]                for (let p = 1; p <= j; p++)                    best = Math.min(best, Math.max(dp[i - 1][p],                                          sum(arr, p, j - 1)));                 dp[i][j] = best;            }        }         // required        return dp[k][n];    }         // Driver code    let arr = [10, 20, 60, 50, 30, 40];      // Calculate size of array.    let n = arr.length;    let k = 3;    document.write(findMax(arr, n, k));         // This code is contributed by mukesh07.</script>
Output: 
90
Optimizations: 
1) The time complexity of the above program is . It can be easily brought down to by precomputing the cumulative sums in an array thus avoiding repeated calls to the sum function:  
C++
Java
Python3
C#
Javascript
int sum[n+1] = {0};  // sum from 1 to i elements of arr for (int i = 1; i <= n; i++)   sum[i] = sum[i-1] + arr[i-1];  for (int i = 1; i <= n; i++)   dp[1][i] = sum[i]; // and using it to calculate the result as:best = min(best, max(dp[i-1][p], sum[j] - sum[p]));
int sum[] = new int[n+1];    // sum from 1 to i elements of arr for (int i = 1; i <= n; i++)   sum[i] = sum[i-1] + arr[i-1];    for (int i = 1; i <= n; i++)   dp[1][i] = sum[i];   // and using it to calculate the result as:best = Math.min(best, Math.max(dp[i-1][p], sum[j] - sum[p])); // This code is contributed by divyesh072019.
sum = [0] * (n + 1) # sum from 1 to i elements of arrfor i in range(1, n + 1):    sum[i] = sum[i-1] + arr[i-1] for i in range(1, n + 1):    dp[1][i] = sum[i] # and using it to calculate the result as:best = min(best, max(dp[i-1][p], sum[j] - sum[p])); # This code is contributed by kraanzu.
int[] sum = new int[n+1];   // sum from 1 to i elements of arr for (int i = 1; i <= n; i++)   sum[i] = sum[i-1] + arr[i-1];   for (int i = 1; i <= n; i++)   dp[1,i] = sum[i];  // and using it to calculate the result as:best = Math.Min(best, Math.Max(dp[i-1][p], sum[j] - sum[p])); // This code is contributed by divyeshrabadiya07.
<script>let sum = new Array(n+1);   // sum from 1 to i elements of arrfor (let i = 1; i <= n; i++)  sum[i] = sum[i-1] + arr[i-1];  for (let i = 1; i <= n; i++)  dp[1][i] = sum[i];  // and using it to calculate the result as:best = Math.min(best, Math.max(dp[i-1][p], sum[j] - sum[p])); // This code is contributed by Saurabh Jaiswal.</script>
2) Though here we consider to divide A into k or fewer partitions, we can observe that the optimal case always occurs when we divide A into exactly k partitions. So we can use: 
C++
Java
Python3
C#
Javascript
for (int i = k-1; i <= n; i++)    best = min(best, max( partition(arr, i, k-1),                            sum(arr, i, n-1)));
for (int i = k-1; i <= n; i++)      best = Math.min(best, Math.max(partition(arr, i, k-1),                              sum(arr, i, n-1))); // This code is contributed by pratham76.
for i range(k-1,n+1):    best=min(best, max(partition(arr, i, k-1),sum(arr, i, n-1)))     # This code is contributed by Aman Kumar.
for(int i = k - 1; i <= n; i++)    best = Math.Min(best, Math.Max(partition(arr, i, k - 1),                                         sum(arr, i, n - 1))); // This code is contributed by rutvik_56
for(var i = k - 1; i <= n; i++)      best = Math.min(best, Math.max(partition(arr, i, k - 1),                                           sum(arr, i, n - 1)));                                            // This code is contributed by Ankita saini
and modify the other implementations accordingly. Exercise: Can you come up with a solution using binary search? Please refer Allocate minimum number of pages for details.References: https://articles.leetcode.com/the-painters-partition-problem/
vt_m
jit_t
sahilshelangia
harshal_97
ashutosh450
__tejas_
divyeshrabadiya07
divyesh072019
pratham76
rutvik_56
mukesh07
susmitakundugoaldanga
ankita_saini
varshagumber28
_saurabh_jaiswal
kraanzu
amankr0211
Binary Search
Codenation
Google
Divide and Conquer
Dynamic Programming
Searching
Google
Codenation
Searching
Dynamic Programming
Divide and Conquer
Binary Search
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Merge Sort
QuickSort
Binary Search
Maximum and minimum of an array using minimum number of comparisons
Count Inversions in an array | Set 1 (Using Merge Sort)
Largest Sum Contiguous Subarray
Program for Fibonacci numbers
Find if there is a path between two vertices in an undirected graph
Longest Increasing Subsequence | DP-3
Longest Palindromic Substring | Set 1