The Painter's Partition Problem (original) (raw)

Given an array **arr[] and an integer **k, where the array represents the boards and each element denotes the length of a board, and k painters are available to paint these boards. Each **unit length of a board takes **1 unit of time to paint. Find the minimum time required to paint all the boards such that each painter paints only **contiguous sections of the array. A painter can paint boards like [2, 3, 4], [1], or even no board, but cannot paint non-contiguous boards like [2, 4, 5].

**Examples:

**Input: arr[] = [5, 10, 30, 20, 15], k = 3
**Output: 35
**Explanation: The most optimal way will be: Painter 1 allocation : [5,10], Painter 2 allocation : [30], Painter 3 allocation : [20, 15], Job will be done when all painters finish i.e. at time = max(5 + 10, 30, 20 + 15) = 35

**Input: arr[] = [10, 20, 30, 40], k = 2
**Output: 60
**Explanation: The most optimal way to paint: Painter 1 allocation : [10, 20, 30], Painter 2 allocation : [40], Job will be complete at time = 60

Table of Content

[Naive Approach] Using recursion - O(n^(k-1)) Time and O(k) Space

A brute force solution is to consider all possible ways to divide the array into at most k contiguous partitions and calculate the maximum sum for each partitioning. The minimum among all such maximum values is returned as the answer.

The recursive function assigns a continuous segment of boards starting from index curr to the current painter. For each possible partition point, it recursively computes the minimum time required for the remaining boards using the remaining painters.

**Recurrence Relation:
Let minTime(curr, k) represent the minimum time to paint the boards from curr to the end with k remaining painters. The recurrence relation is:

Where:
sum(curr, i) is the total time for the current painter to paint boards from index curr to i where i can range from curr to n - k.
minTime(i + 1, k - 1) is the recursive call for the remaining boards and painters.

**Base Cases:

C++ `

#include #include #include #include using namespace std;

int minimizeTime(int curr, int n, vector &arr, int k) {

// If all boards are painted, return 0
if (curr >= n)
    return 0;

// If no painters are left
if (k == 0)
    return INT_MAX;

// If only one painter remains, assign all remaining boards
if (k == 1) {
    int sum = 0;
    for (int i = curr; i < n; i++)
        sum += arr[i];
    return sum;
}

// Current workload for this painter int currSum = 0;

// Result to store the minimum possible time int res = INT_MAX;

// Divide the boards among painters starting from curr
for (int i = curr; i <= n - k; i++) {
    currSum += arr[i];

 // Find the maximum time if we assign arr[curr..i] to
 // this painter
    int remTime = minimizeTime(i + 1, n, arr, k - 1);

    if (remTime != INT_MAX) {
        int remaining = max(currSum, remTime);

        // Update the result
        res = min(res, remaining);
    }
}

return res;

}

int minTime(vector &arr, int k) { int n = arr.size(); return minimizeTime(0, n, arr, k); }

int main() { vector arr = {5, 10, 30, 20, 15}; int k = 3; int res = minTime(arr, k); cout << res << endl;

return 0;

}

Java

import java.util.*;

class GfG {

static int minimizeTime(int curr, int n, int[] arr,
                        int k) {

    // If all boards are painted, return 0
    if (curr >= n)
        return 0;

    // If no painters are left
    if (k == 0)
        return Integer.MAX_VALUE;

    // If only one painter remains, assign all remaining boards
    if (k == 1) {
        int sum = 0;
        for (int i = curr; i < n; i++)
            sum += arr[i];
        return sum;
    }

   // Current workload for this painter
    int currSum = 0;    

   // Result to store the minimum possible time
    int res = Integer.MAX_VALUE;  

    // Divide the boards among painters starting from curr
    for (int i = curr; i <= n - k; i++) {
        currSum += arr[i];

     // Find the maximum time if we assign arr[curr..i] to
     // this painter
        int remTime
            = minimizeTime(i + 1, n, arr, k - 1);

        if (remTime != Integer.MAX_VALUE) {
            int remaining = Math.max(currSum, remTime);

            // Update the result
            res = Math.min(res, remaining);
        }
    }

    return res;
}

static int minTime(int[] arr, int k) {
    int n = arr.length;
    return minimizeTime(0, n, arr, k);
}

public static void main(String[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int res = minTime(arr, k);
    System.out.println(res);
}

}

Python

def minimizeTime(curr, n, arr, k):

# If all boards are painted, return 0
if curr >= n:
    return 0   

# If no painters are left
if k == 0:
    return float('inf')  

# If only one painter remains, assign all remaining boards
if k == 1:
    sum = 0
    for i in range(curr, n):
        sum += arr[i]
    return sum

Current workload for this painter

currSum = 0  

Result to store the minimum possible time

res = float('inf')  

# Divide the boards among painters starting from curr
for i in range(curr, n - k + 1):
    currSum += arr[i]

 # Find the maximum time if we assign arr[curr..i] to
 # this painter
    remTime = minimizeTime(i + 1, n, arr, k - 1)

    if remTime != float('inf'):
        remaining = max(currSum, remTime)

        # Update the result
        res = min(res, remaining)

return res

def minTime(arr, k): n = len(arr) return minimizeTime(0, n, arr, k)

if name == "main": arr = [5, 10, 30, 20, 15] k = 3 res = minTime(arr, k) print(res)

C#

using System;

class GfG {

static int minimizeTime(int curr, int n, int[] arr,
                        int k) {

    // If all boards are painted, return 0
    if (curr >= n)
        return 0;

    // If no painters are left
    if (k == 0)
        return int.MaxValue;

    // If only one painter remains, assign all remaining boards
    if (k == 1) {
        int sum = 0;
        for (int i = curr; i < n; i++)
            sum += arr[i];
        return sum;
    }

   // Current workload for this painter
    int currSum = 0;

   // Result to store the minimum possible time
    int res = int.MaxValue;

    // Divide the boards among painters starting from curr
    for (int i = curr; i <= n - k; i++) {
        currSum += arr[i];

     // Find the maximum time if we assign arr[curr..i] to
     // this painter
        int remTime
            = minimizeTime(i + 1, n, arr, k - 1);

        if (remTime != int.MaxValue) {
            int remaining = Math.Max(currSum, remTime);

            // Update the result
            res = Math.Min(res, remaining);
        }
    }

    return res;
}

static int minTime(int[] arr, int k) {
    int n = arr.Length;
    return minimizeTime(0, n, arr, k);
}

static void Main(string[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int res = minTime(arr, k);
    Console.WriteLine(res);
}

}

JavaScript

function minimizeTime(curr, n, arr, k) {

// If all boards are painted, return 0
if (curr >= n)
    return 0;

// If no painters are left
if (k === 0)
    return Infinity;

// If only one painter remains, assign all remaining boards
if (k === 1) {
    let sum = 0;
    for (let i = curr; i < n; i++)
        sum += arr[i];
    return sum;
}

// Current workload for this painter let currSum = 0;

// Result to store the minimum possible time let res = Infinity;

// Divide the boards among painters starting from curr
for (let i = curr; i <= n - k; i++) {
    currSum += arr[i];

 // Find the maximum time if we assign arr[curr..i] to
 // this painter
    let remTime = minimizeTime(i + 1, n, arr, k - 1);

    if (remTime !== Infinity) {
        let remaining = Math.max(currSum, remTime);

        // Update the result
        res = Math.min(res, remaining);
    }
}

return res;

}

function minTime(arr, k) { const n = arr.length; return minimizeTime(0, n, arr, k); }

// Driver Code const arr = [5, 10, 30, 20, 15]; const k = 3; const res = minTime(arr, k); console.log(res);

`

[Better Approach - 1] Using Memoization - O(n*n*k) Time and O(n*k) Space

If we closely observe the recursive function **minTime(), it solves the overlapping subproblems multiple times in different recursive paths, leading to redundant computations. This redundancy can be avoided by applying memoization. Since the state of the recursion is defined by the current index (curr) and the number of remaining painters (k), we use a 2D array of size **n x (k+1) to store results. The array is initialized with -1 to indicate uncomputed states, so previously solved subproblems can be reused.

C++ `

#include #include #include #include using namespace std;

int minimizeTime(int curr, int n, vector &arr, int k, vector<vector> &memo) {

// If all boards are painted, return 0
if (curr >= n)
    return 0;

// If no painters are left
if (k == 0)
    return INT_MAX;
    
// If only one painter remains, assign all remaining boards
if (k == 1) {
    int sum = 0;
    for (int i = curr; i < n; i++)
        sum += arr[i];
    return sum;
}

// Check if the result is already computed and stored in memo table
// If so, return the stored result to avoid recomputation
if (memo[curr][k] != -1)
    return memo[curr][k];

// Current workload for this painter
int currSum = 0;    

// Result to store the minimum possible time
int res = INT_MAX;  

// Divide the boards among painters starting from curr
for (int i = curr; i <= n - k; i++) {
    currSum += arr[i];

    // Find the maximum time if we assign arr[curr..i] to
    // this painter
    int remTime = minimizeTime(i + 1, n, arr, k - 1, memo);

    if (remTime != INT_MAX) {
        int remaining = max(currSum, remTime);

        // Update the result
        res = min(res, remaining);
    }
}

// Store the computed result in the memo table
// This helps avoid redundant calculations in future calls
return memo[curr][k] = res;

}

int minTime(vector &arr, int k) { int n = arr.size();

// Initialize memoization table with -1
  // (indicating no result computed yet)
vector<vector<int>> memo(n, vector<int>(k + 1, -1));
return minimizeTime(0, n, arr, k, memo);

}

int main() {

vector<int> arr = {5, 10, 30, 20, 15};
int k = 3;
int res = minTime(arr, k);
cout << res << endl;

return 0;

}

Java

import java.util.*;

class GfG {

static int minimizeTime(int curr, int n, int[] arr,
                        int k, int[][] memo) {

    // If all boards are painted, return 0
    if (curr >= n) {
        return 0;
    }

    // If no painters are left
    if (k == 0) {
        return Integer.MAX_VALUE;
    }

    // If only one painter remains, assign all remaining boards
    if (k == 1) {
        int sum = 0;
        for (int i = curr; i < n; i++)
            sum += arr[i];
        return sum;
    }

    // Check if the result is already computed and stored in memo table
    // If so, return the stored result to avoid recomputation
    if (memo[curr][k] != -1) {
        return memo[curr][k];
    }

    // Current workload for this painter
    int currSum = 0;

    // Result to store the minimum possible time
    int res = Integer.MAX_VALUE;

    // Divide the boards among painters starting from curr
    for (int i = curr; i <= n - k; i++) {
        currSum += arr[i];

        // Find the maximum time if we assign arr[curr..i] to
        // this painter
        int remTime
            = minimizeTime(i + 1, n, arr, k - 1, memo);

        if (remTime != Integer.MAX_VALUE) {
            int remaining = Math.max(currSum, remTime);

            // Update the result
            res = Math.min(res, remaining);
        }
    }

    // Store the computed result in the memo table
    // This helps avoid redundant calculations in future calls
    memo[curr][k] = res;
    return res;
}

static int minTime(int[] arr, int k) {
    int n = arr.length;

    // Initialize memoization table with -1
      // (indicating no result computed yet)
    int[][] memo = new int[n][k + 1];
    for (int[] row : memo) {
        Arrays.fill(row, -1);
    }

    return minimizeTime(0, n, arr, k, memo);
}

public static void main(String[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int res = minTime(arr, k);
    System.out.println(res);
}

}

Python

def minimizeTime(curr, n, arr, k, memo):

# If all boards are painted, return 0
if curr >= n:
    return 0

# If no painters are left
if k == 0:
    return float('inf')

# If only one painter remains, assign all remaining boards
if k == 1:
    sum = 0
    for i in range(curr, n):
        sum += arr[i]
    return sum

# Check if the result is already computed and stored in memo table
# If so, return the stored result to avoid recomputation
if memo[curr][k] != -1:
    return memo[curr][k]

# Current workload for this painter
currSum = 0

# Result to store the minimum possible time
res = float('inf')

# Divide the boards among painters starting from curr
for i in range(curr, n - k + 1):
    currSum += arr[i]

    # Find the maximum time if we assign arr[curr..i] to
    # this painter
    remTime = minimizeTime(i + 1, n, arr, k - 1, memo)

    if remTime != float('inf'):
        remaining = max(currSum, remTime)

        # Update the result
        res = min(res, remaining)

# Store the computed result in the memo table
# This helps avoid redundant calculations in future calls
memo[curr][k] = res
return res

def minTime(arr, k): n = len(arr)

# Initialize memoization table with -1
# (indicating no result computed yet)
memo = [[-1] * (k + 1) for _ in range(n)]
return minimizeTime(0, n, arr, k, memo)

if name == "main": arr = [5, 10, 30, 20, 15] k = 3 res = minTime(arr, k) print(res)

C#

using System;

class GfG {

static int minimizeTime(int curr, int n, int[] arr,
                        int k, int[, ] memo) {

    // If all boards are painted, return 0
    if (curr >= n)
        return 0;

    // If no painters are left
    if (k == 0)
        return int.MaxValue;

    // If only one painter remains, assign all remaining boards
    if (k == 1) {
        int sum = 0;
        for (int i = curr; i < n; i++)
            sum += arr[i];
        return sum;
    }

    // Check if the result is already computed and stored in memo table
    // If so, return the stored result to avoid recomputation
    if (memo[curr, k] != -1)
        return memo[curr, k];

    // Current workload for this painter
    int currSum = 0;

    // Result to store the minimum possible time
    int res = int.MaxValue;

    // Divide the boards among painters starting from curr
    for (int i = curr; i <= n - k; i++) {
        currSum += arr[i];

        // Find the maximum time if we assign arr[curr..i] to
        // this painter
        int remTime
            = minimizeTime(i + 1, n, arr, k - 1, memo);

        if (remTime != int.MaxValue) {
            int remaining = Math.Max(currSum, remTime);

            // Update the result
            res = Math.Min(res, remaining);
        }
    }

    // Store the computed result in the memo table
    // This helps avoid redundant calculations in future calls
    memo[curr, k] = res;
    return res;
}

static int minTime(int[] arr, int k) {
    int n = arr.Length;

    // Initialize memoization table with -1
    // (indicating no result computed yet)
    int[, ] memo = new int[n, k + 1];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= k; j++) {
            memo[i, j] = -1;
        }
    }

    return minimizeTime(0, n, arr, k, memo);
}

static void Main(string[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int result = minTime(arr, k);
    Console.WriteLine(result);
}

}

JavaScript

function minimizeTime(curr, n, arr, k, memo) {

// If all boards are painted, return 0
if (curr >= n)
    return 0;

// If no painters are left
if (k === 0)
    return Number.POSITIVE_INFINITY;

// If only one painter remains, assign all remaining boards
if (k === 1) {
    let sum = 0;
    for (let i = curr; i < n; i++)
        sum += arr[i];
    return sum;
}

// Check if the result is already computed and stored in memo table
// If so, return the stored result to avoid recomputation
if (memo[curr][k] !== -1)
    return memo[curr][k];

// Current workload for this painter
let currSum = 0;

// Result to store the minimum possible time
let res = Number.POSITIVE_INFINITY;

// Divide the boards among painters starting from curr
for (let i = curr; i <= n - k; i++) {
    currSum += arr[i];

    // Find the maximum time if we assign arr[curr..i] to
    // this painter
    let remTime
        = minimizeTime(i + 1, n, arr, k - 1, memo);

    if (remTime !== Number.POSITIVE_INFINITY) {
        let remaining = Math.max(currSum, remTime);

        // Update the result
        res = Math.min(res, remaining);
    }
}

// Store the computed result in the memo table
// This helps avoid redundant calculations in future calls
memo[curr][k] = res;
return res;

}

function minTime(arr, k) { const n = arr.length;

// Initialize memoization table with -1
// (indicating no result computed yet)
let memo = Array.from({length : n},
                      () => Array(k + 1).fill(-1));

return minimizeTime(0, n, arr, k, memo);

}

const arr = [5, 10, 30, 20, 15]; const k = 3; const res = minTime(arr, k); console.log(res);

`

[Better Approach - 2] Using Tabulation - O(n*n*k) Time and O(n*k) Space

The approach is similar to the previous one. Instead of breaking down the problem recursively, we iteratively build up the solution by calculating in bottom-up manner.

We create a 2D array dp, where **dp[i][j] represents the minimum time required to paint boards starting from index i to the last board (n-1) using j painters.

The recurrence relation for filling this DP table is as follows:

Where:

We take the maximum of sum(i, i1) and dp[i1 + 1][j - 1] because the time taken by the current painter is limited by the maximum time among the painters, and the goal is to minimize the maximum time.

**Base Case:

C++ `

#include #include #include using namespace std;

int minTime(vector &arr, int k) {

int n = arr.size();
vector<int> pre(n, 0);

// Calculate the prefix sum array
pre = vector<int>(n + 1, 0);
pre[0] = arr[0];
for (int i = 1; i < n; i++) {
    pre[i] = pre[i - 1] + arr[i];
}

// DP table to store the minimum time for subproblems
vector<vector<int>> dp(n + 1, vector<int>(k + 1, 1e9));

// Base case: if there are no boards to paint, the time is 0
for (int i = 0; i <= k; i++) {
    dp[n][i] = 0;
}

// Fill the DP table by iterating over the boards and painters
for (int i = n - 1; i >= 0; i--) {
    for (int j = 1; j <= k; j++) {

        // Base case: only one painter
        if (j == 1) {
            dp[i][j] = pre[n - 1] - (i > 0 ? pre[i - 1] : 0);
            continue;
        }

        for (int i1 = i; i1 <= n - j; i1++) {

            // Calculate the sum directly using the prefix sum array
            int currSum = pre[i1] - (i > 0 ? pre[i - 1] : 0);

            // Transition: take the maximum of the
            // current sum and the result for
            // remaining painters
            dp[i][j] = min(dp[i][j], max(currSum, dp[i1 + 1][j - 1]));
        }
    }
}

// Return the minimum time for painting all boards
// starting from index 0 with k painters
return dp[0][k];

}

int main() {

vector<int> arr = {5, 10, 30, 20, 15};
int k = 3;
int res = minTime(arr, k);
cout << res;
return 0;

}

Java

import java.util.*;

class GfG {

static int minTime(int[] arr, int k) {
    int n = arr.length;

    // Prefix sum array to store the cumulative sums of
    // boards
    int[] pre = new int[n];

    // Calculate the prefix sum array
    pre = new int[n + 1];
    pre[0] = arr[0];
    for (int i = 1; i < n; i++) {
        pre[i] = pre[i - 1] + arr[i];
    }

    // DP table to store the minimum time for
    // subproblems
    int[][] dp = new int[n + 1][k + 1];

    // Initialize DP table with a large value
    for (int i = 0; i <= n; i++) {
        Arrays.fill(dp[i], (int)1e9);
    }

    // Base case: if there are no boards to paint, the time is 0
    for (int i = 0; i <= k; i++) {
        dp[n][i] = 0;
    }

    // Fill the DP table by iterating over the boards and painters
    for (int i = n - 1; i >= 0; i--) {
        for (int j = 1; j <= k; j++) {

            // Base case: only one painter
            if (j == 1) {
                dp[i][j] = pre[n - 1] - (i > 0 ? pre[i - 1] : 0);
                continue;
            }

            for (int i1 = i; i1 <= n - j; i1++) {
              
                // Calculate the sum directly using the prefix sum array
                int currSum = pre[i1] - (i > 0 ? pre[i - 1] : 0);

                // Transition: take the maximum of the
                // current sum and the result for
                // remaining painters
                dp[i][j] = Math.min(
                    dp[i][j],
                    Math.max(currSum,
                             dp[i1 + 1][j - 1]));
            }
        }
    }

    // Return the minimum time for painting all boards
    // starting from index 0 with k painters
    return dp[0][k];
}

public static void main(String[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int res = minTime(arr, k);
    System.out.println(res);
}

}

Python

def minTime(arr, k): n = len(arr)

# Calculate the prefix sum array
pre = [0] * n

pre = [0] * (n + 1)
pre[0] = arr[0]
for i in range(1, n):
    pre[i] = pre[i - 1] + arr[i]

# DP table to store the minimum time for subproblems
dp = [[int(1e9)] * (k + 1) for _ in range(n + 1)]

# Base case: if there are no boards to paint, the time is 0
for i in range(k + 1):
    dp[n][i] = 0

# Fill the DP table by iterating over the boards and painters
for i in range(n - 1, -1, -1):
    for j in range(1, k + 1):

        # Base case: only one painter
        if j == 1:
            dp[i][j] = pre[n - 1] - (pre[i - 1] if i > 0 else 0)
            continue

        for i1 in range(i, n - j + 1):

            # Calculate the sum directly using the prefix sum array
            currSum = pre[i1] - (pre[i - 1] if i > 0 else 0)

            # Transition: take the maximum of the
            # current sum and the result for
            # remaining painters
            dp[i][j] = min(dp[i][j], max(currSum, dp[i1 + 1][j - 1]))

# Return the minimum time for painting all boards
# starting from index 0 with k painters
return dp[0][k]

arr = [5, 10, 30, 20, 15] k = 3 res = minTime(arr, k) print(res)

C#

using System;

class GfG { static int minTime(int[] arr, int k) { int n = arr.Length;

    // Calculate the prefix sum array
    int[] pre = new int[n];

    pre = new int[n + 1];
    pre[0] = arr[0];
    for (int i = 1; i < n; i++) {
        pre[i] = pre[i - 1] + arr[i];
    }

    // DP table to store the minimum time for subproblems
    int[, ] dp = new int[n + 1, k + 1];

    // Initialize the DP table with a large value
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            dp[i, j] = (int)1e9;
        }
    }

    // Base case: if there are no boards to paint, the time is 0
    for (int i = 0; i <= k; i++) {
        dp[n, i] = 0;
    }

    // Fill the DP table by iterating over the boards and painters
    for (int i = n - 1; i >= 0; i--) {
        for (int j = 1; j <= k; j++) {

            // Base case: only one painter
            if (j == 1) {
                dp[i, j] = pre[n - 1] - (i > 0 ? pre[i - 1] : 0);
                continue;
            }

            for (int i1 = i; i1 <= n - j; i1++) {
              
                // Calculate the sum directly using the prefix sum array
                int currSum = pre[i1] - (i > 0 ? pre[i - 1] : 0);

                // Transition: take the maximum of the
                // current sum and the result for
                // remaining painters
                dp[i, j] = Math.Min(
                    dp[i, j], Math.Max(currSum, dp[i1 + 1, j - 1]));
            }
        }
    }

    // Return the minimum time for painting all boards
    // starting from index 0 with k painters
    return dp[0, k];
}

static void Main() {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int res = minTime(arr, k);
    Console.WriteLine(res);
}

}

JavaScript

function minTime(arr, k) { let n = arr.length;

// Calculate the prefix sum array
let pre = new Array(n).fill(0);

pre = new Array(n + 1).fill(0);
pre[0] = arr[0];
for (let i = 1; i < n; i++) {
    pre[i] = pre[i - 1] + arr[i];
}

// DP table to store the minimum time for subproblems
let dp = Array.from(
    {length : n + 1},
    () => new Array(k + 1).fill(1e9));

// Base case: if there are no boards to paint, the time is 0
for (let i = 0; i <= k; i++) {
    dp[n][i] = 0;
}

// Fill the DP table by iterating over the boards and painters
for (let i = n - 1; i >= 0; i--) {
    for (let j = 1; j <= k; j++) {

        // Base case: only one painter
        if (j === 1) {
            dp[i][j] = pre[n - 1] - (i > 0 ? pre[i - 1] : 0);
            continue;
        }

        for (let i1 = i; i1 <= n - j; i1++) {
        
            // Calculate the sum directly using the prefix sum array
            let currSum = pre[i1] - (i > 0 ? pre[i - 1] : 0);

            // Transition: take the maximum of the
            // current sum and the result for
            // remaining painters
            dp[i][j] = Math.min(
                dp[i][j],
                Math.max(currSum, dp[i1 + 1][j - 1]));
        }
    }
}

// Return the minimum time for painting all boards
// starting from index 0 with k painters
return dp[0][k];

}

// Driver Code let arr = [5, 10, 30, 20, 15]; let k = 3; let res = minTime(arr, k); console.log(res);

`

The idea is to apply binary search on the answer to find the minimum time required to paint all boards using at most k painters.

The search space lies between:

For each mid value, we perform a greedy feasibility check:

If all boards can be painted using at most k painters, the time is feasible.

The problem exhibits a monotonic property:

This forms a monotonic boolean function, which makes the search space ordered and allows us to efficiently apply binary search to find the minimum feasible time.

Input: arr = [5, 10, 30, 20, 15], k = 3, Search space: low = 30, high = 80

Minimum time to paint all boards = 35

C++ `

#include #include #include #include using namespace std;

// checks whether all boards can be painted within 'maxTime' // by dividing the work among at most k painters bool isPossible(int maxTime, vector& arr, int k) { int painters = 1;
int currSum = 0;

for (int length : arr) {
    
    // if a board is longer than maxTime,
    // it's impossible to assign
    if (length > maxTime)
        return false;

    // if assigning this board exceeds maxTime, 
    // give it to a new painter
    if (currSum + length > maxTime) {
        painters++;        
        currSum = length;  
    } 
    
    // otherwise, continue adding to the current 
    // painter's workload
    else {
        currSum += length;
    }
}

// return true if total painters used is
// within the allowed k
return painters <= k;

}

int minTime(vector& arr, int k) {

// lower limit is the largest board 
// (can't split boards)
int low = *max_element(arr.begin(), arr.end());

// upper limit is the sum of all board lengths 
// (one painter does all)
int high = accumulate(arr.begin(), arr.end(), 0);

int result = high;

while (low <= high) {
    int mid = (low + high) / 2;

    // if this time allows us to paint
    // with k painters or fewer
    if (isPossible(mid, arr, k)) {
        result = mid;     
        high = mid - 1;   
    } 
    
    // if not possible, we need to allow 
    // more time
    else {
        low = mid + 1;
    }
}

return result;

}

int main() {

vector<int> arr = {5, 10, 30, 20, 15};
int k = 3;
int result = minTime(arr, k);
cout << result << endl;

return 0;

}

Java

import java.util.*;

class GfG {

// checks whether all boards can be painted within 'maxTime'
// by dividing the work among at most k painters
static boolean isPossible(int maxTime, int[] arr, int k) {
    int painters = 1;
    int currSum = 0;

    for (int length : arr) {
        
        // if a board is longer than maxTime,
        // it's impossible to assign
        if (length > maxTime)
            return false;

        // if assigning this board exceeds maxTime, 
        // give it to a new painter
        if (currSum + length > maxTime) {
            painters++;
            currSum = length;
        } 
        
        // otherwise, continue adding to the current 
        // painter's workload
        else {
            currSum += length;
        }
    }

    // return true if total painters used is
    // within the allowed k
    return painters <= k;
}

static int minTime(int[] arr, int k) {
    int low = Arrays.stream(arr).max().getAsInt();
    int high = Arrays.stream(arr).sum();
    int result = high;

    while (low <= high) {
        int mid = (low + high) / 2;

        // if this time allows us to paint
        // with k painters or fewer
        if (isPossible(mid, arr, k)) {
            result = mid;
            high = mid - 1;
        } 
        
        // if not possible, we need to allow 
        // more time
        else {
            low = mid + 1;
        }
    }

    return result;
}

public static void main(String[] args) {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int result = minTime(arr, k);
    System.out.println(result);
}

}

Python

checks whether all boards can be painted within 'maxTime'

by dividing the work among at most k painters

def isPossible(maxTime, arr, k): painters = 1 currSum = 0

for length in arr:
    
    # if a board is longer than maxTime,
    # it's impossible to assign
    if length > maxTime:
        return False

    # if assigning this board exceeds maxTime, 
    # give it to a new painter
    if currSum + length > maxTime:
        painters += 1
        currSum = length
    
    # otherwise, continue adding to the current 
    # painter's workload
    else:
        currSum += length

# return true if total painters used is
# within the allowed k
return painters <= k

def minTime(arr, k): low = max(arr) high = sum(arr) result = high

while low <= high:
    mid = (low + high) // 2

    # if this time allows us to paint
    # with k painters or fewer
    if isPossible(mid, arr, k):
        result = mid
        high = mid - 1
        
    # if not possible, we need to allow 
    # more time
    else:
        low = mid + 1

return result

if name == "main": arr = [5, 10, 30, 20, 15] k = 3 result = minTime(arr, k) print(result)

C#

using System; using System.Linq;

class GfG {

// checks whether all boards can be painted within 'maxTime'
// by dividing the work among at most k painters
static bool isPossible(int maxTime, int[] arr, int k) {
    int painters = 1;
    int currSum = 0;

    foreach (int length in arr) {
        
        // if a board is longer than maxTime,
        // it's impossible to assign
        if (length > maxTime)
            return false;

        // if assigning this board exceeds maxTime, 
        // give it to a new painter
        if (currSum + length > maxTime) {
            painters++;
            currSum = length;
        }
        
        // otherwise, continue adding to the current 
        // painter's workload
        else {
            currSum += length;
        }
    }

    // return true if total painters used is
    // within the allowed k
    return painters <= k;
}

static int minTime(int[] arr, int k) {
    int low = arr.Max();
    int high = arr.Sum();
    int result = high;

    while (low <= high) {
        int mid = (low + high) / 2;

        // if this time allows us to paint
        // with k painters or fewer
        if (isPossible(mid, arr, k)) {
            result = mid;
            high = mid - 1;
        }
        
        // if not possible, we need to allow 
        // more time
        else {
            low = mid + 1;
        }
    }

    return result;
}

static void Main() {
    int[] arr = {5, 10, 30, 20, 15};
    int k = 3;
    int result = minTime(arr, k);
    Console.WriteLine(result);
}

}

JavaScript

// checks whether all boards can be painted within 'maxTime' // by dividing the work among at most k painters function isPossible(maxTime, arr, k) { let painters = 1; let currSum = 0;

for (let length of arr) {
    
    // if a board is longer than maxTime,
    // it's impossible to assign
    if (length > maxTime)
        return false;

    // if assigning this board exceeds maxTime, 
    // give it to a new painter
    if (currSum + length > maxTime) {
        painters++;
        currSum = length;
    }
    
    // otherwise, continue adding to the current 
    // painter's workload
    else {
        currSum += length;
    }
}

// return true if total painters used is
// within the allowed k
return painters <= k;

}

function minTime(arr, k) { let low = Math.max(...arr); let high = arr.reduce((a, b) => a + b, 0); let result = high;

while (low <= high) {
    let mid = Math.floor((low + high) / 2);

    // if this time allows us to paint
    // with k painters or fewer
    if (isPossible(mid, arr, k)) {
        result = mid;
        high = mid - 1;
    }
    
    // if not possible, we need to allow 
    // more time
    else {
        low = mid + 1;
    }
}

return result;

}

// Driver Code const arr = [5, 10, 30, 20, 15]; const k = 3; const result = minTime(arr, k); console.log(result);

`

**Time Complexity: O(n × log(sum(arr))), Binary search is applied over the range from max(arr) to sum(arr), resulting in O(log(sum(arr))) iterations. In each iteration, a greedy feasibility check scans the array once, taking O(n) time.
**Auxiliary Space: O(1)