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) = 35Input: 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.Â
Try every possible way to assign a continuous segment of boards to the current painter. Recursively solve the remaining boards for the remaining painters and choose the partition that minimizes the maximum painting time.
- Define minTime(curr, k) as the minimum time to paint boards from index curr using k painters.
- Assign the current painter every possible continuous segment starting from curr.
- Recursively compute the minimum time for the remaining boards with k - 1 painters.
- For each partition, the required time is: max(time taken by current painter, time for remaining painters).
- Return the minimum value among all possible partitions.
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
int minimizeTime(int curr, int n, vector<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_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<int> &arr, int k) {
int n = arr.size();
return minimizeTime(0, n, arr, k);
}
int main() {
vector<int> arr = {5, 10, 30, 20, 15};
int k = 3;
int res = minTime(arr, k);
cout << res << endl;
return 0;
}
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);
}
}
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)
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);
}
}
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);
Output
35
[Better Approach - 1] Using Memoization - O(n*n*k) Time and O(n*k) Space
The recursive solution solves the same subproblems multiple times. Store the result of each state (curr, k) so that it is computed only once, eliminating redundant recursive calls.
- Use a 2D DP array dp[n][k + 1] initialized with -1.
- Each state dp[curr][k] stores the minimum time to paint boards from index curr using k painters.
- Before solving a state, check if its value is already stored in dp.
- If available, return the stored value; otherwise, compute it recursively and store the result before returning.
- The recurrence relation and base cases remain the same as the recursive approach.
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
int minimizeTime(int curr, int n, vector<int> &arr, int k,
vector<vector<int>> &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<int> &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;
}
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);
}
}
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)
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);
}
}
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);
Output
35
[Better Approach - 2] Using Tabulation - O(n*n*k) Time and O(n*k) Space
Build the solution iteratively by storing the answer for every state (curr, k) in a DP table. Each state is computed using previously solved smaller subproblems, eliminating recursion.
- Create a 2D DP array dp[n + 1][k + 1], where dp[i][j] stores the minimum time to paint boards from index i using j painters.
- Compute prefix sums to obtain the sum of any board segment in O(1) time.
- Fill the DP table bottom-up.
- For each state, try every possible partition for the current painter and compute: max(time taken by current painter, time for remaining painters).
- Store the minimum value among all possible partitions in dp[i][j].
- dp[n][j] = 0 for all j.
- dp[i][1] = sum(i, n - 1) for all i.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int minTime(vector<int> &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;
}
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);
}
}
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)
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);
}
}
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);
Output
35
[Expected Approach] Using Binary Search
Apply binary search on the maximum time assigned to a painter. For each candidate time, greedily check whether all boards can be painted using at most k painters while maintaining continuous board assignments.
- Set Low = maximum board length and High = sum of all board lengths.
- Perform binary search on this range.
- For each mid, greedily assign boards to the current painter.
- If adding a board exceeds mid, assign it to a new painter.
- If all boards can be painted using at most k painters, store mid as a possible answer and search for a smaller value.
- Otherwise, search for a larger value.
- Return the minimum feasible time found.
Dry Run on arr = [5, 10, 30, 20, 15], k = 3:
Search space: low = 30, high = 80
- mid = 55
Painters: [5,10,30] = 45, [20,15] = 35 , 2 painters
Feasible, update high = 54 - mid = 42
Painters: [5,10] = 15, [30] = 30, [20,15] = 35, 3 painters
Feasible, update high = 41 - mid = 35
Painters: [5,10,20] = 35, [30], [15], 3 painters
Feasible, update high = 34 - mid = 32
Needs 4 painters
Not feasible, update low = 33 - mid = 33, 34
Not feasible, update low = 35
Minimum time to paint all boards = 35
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
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<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;
}
int minTime(vector<int>& 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;
}
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);
}
}
# 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)
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);
}
}
// 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);
Output
35
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)