Split into K subarrays to minimize the maximum sum of all subarrays
Given an array arr[] and a number k, split the given array into k subarrays such that the maximum subarray sum achievable out of k subarrays formed is the minimum possible, find that possible subarray sum.
Examples:
Input: arr[] = [1, 2, 3, 4], k = 3
Output: 4
Explanation: Optimal Split is [1, 2], [3], [4]. Maximum sum of all subarrays is 4, which is minimum possible for 3 splits.Input: arr[] = [1, 1, 2], k = 2
Output: 2
Explanation: Splitting the array as [1, 1] and [2] is optimal. This results is a maximum sum subarray of 2.
Table of Content
[Naive Approach] - By iterating through every possible solution
The idea is to recursively try all ways to split the array into
k
contiguous subarrays, tracking the maximum sum in each split. At each step, we pick a new partition point and update the current max sum. Once only one partition remains, the rest of the elements are grouped, and the overall minimum of the maximum subarray sums is recorded.
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
void solve(vector<int> &arr, int k, int index, int maxsum, int &ans) {
// K = 1 is the base Case
int n = arr.size();
int sum = 0;
if (k == 1) {
for (int i = index; i < n; i++) {
sum += arr[i];
}
// we update maxsum
maxsum = max(maxsum, sum);
ans = min(ans, maxsum);
return;
}
// using for loop to divide the array into K-subarray
for (int i = index; i < n; i++) {
sum += arr[i];
// for each subarray we calculate sum ans update
// maxsum
maxsum = max(maxsum, sum);
solve(arr, k - 1, i + 1, maxsum, ans);
}
}
int splitArray(vector<int> &arr, int k) {
int ans = INT_MAX;
solve(arr, k, 0, 0, ans);
return ans;
}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 3;
cout << splitArray(arr, k);
}
// Java code to iterate through
// every possible solution
import java.util.*;
class GfG {
static void solve(int[] arr, int k, int index,
int maxsum, int[] ans) {
// K=1 is the base Case
int n = arr.length;
int sum = 0;
if (k == 1) {
for (int i = index; i < n; i++) {
sum += arr[i];
}
// we update maxsum
maxsum = Math.max(maxsum, sum);
ans[0] = Math.min(ans[0], maxsum);
return;
}
// using for loop to divide the array into
// K-subarray
for (int i = index; i < n; i++) {
sum += arr[i];
// for each subarray we calculate sum and update
// maxsum
maxsum = Math.max(maxsum, sum);
solve(arr, k - 1, i + 1, maxsum, ans);
}
}
static int splitArray(int[] arr, int k) {
int[] ans = { Integer.MAX_VALUE };
solve(arr, k, 0, 0, ans);
return ans[0];
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 3;
System.out.println(splitArray(arr, k));
}
}
# Python code to iterate through
# every possible solution
def solve(arr, k, index, maxsum, ans):
# K=1 is the base Case
n = len(arr)
sum = 0
if k == 1:
for i in range(index, n):
sum += arr[i]
# we update maxsum
maxsum = max(maxsum, sum)
# the answer is stored in ans
ans[0] = min(ans[0], maxsum)
return
# using for loop to divide the array into K-subarray
for i in range(index, n):
sum += arr[i]
# for each subarray we calculate sum and update
# maxsum
maxsum = max(maxsum, sum)
solve(arr, k - 1, i + 1, maxsum, ans)
def splitArray(arr, k):
ans = [float('inf')]
solve(arr, k, 0, 0, ans)
return ans[0]
arr = [1, 2, 3, 4]
k = 3
print(splitArray(arr, k))
// C# code to iterate through
// every possible solution
using System;
class GfG {
static void Solve(int[] arr, int k, int index,
int maxsum, ref int ans) {
// K=1 is the base Case
int n = arr.Length;
int sum = 0;
if (k == 1) {
for (int i = index; i < n; i++) {
sum += arr[i];
}
// we update maxsum
maxsum = Math.Max(maxsum, sum);
ans = Math.Min(ans, maxsum);
return;
}
// using for loop to divide the array into
// K-subarray
for (int i = index; i < n; i++) {
sum += arr[i];
// for each subarray we calculate sum and update
// maxsum
maxsum = Math.Max(maxsum, sum);
Solve(arr, k - 1, i + 1, maxsum, ref ans);
}
}
static int SplitArray(int[] arr, int k) {
int ans = int.MaxValue;
Solve(arr, k, 0, 0, ref ans);
return ans;
}
static void Main(string[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 3;
Console.WriteLine(SplitArray(arr, k));
}
}
function solve(arr, k, index, maxsum, ans) {
// K=1 is the base Case
const n = arr.length;
let sum = 0;
if (k === 1) {
for (let i = index; i < n; i++) {
sum += arr[i];
}
// we update maxsum
maxsum = Math.max(maxsum, sum);
ans[0] = Math.min(ans[0], maxsum);
return;
}
// using for loop to divide the array into K-subarray
for (let i = index; i < n; i++) {
sum += arr[i];
// for each subarray we calculate sum and update
// maxsum
maxsum = Math.max(maxsum, sum);
solve(arr, k - 1, i + 1, maxsum, ans);
}
}
function splitArray(arr, k) {
let ans = [ Infinity ];
solve(arr, k, 0, 0, ans);
return ans[0];
}
// Driver Code
const arr = [ 1, 2, 3, 4 ];
const k = 3;
console.log(splitArray(arr, k));
Output
4
Time Complexity: O((n-1)C(k-1)), to explores all ways to split n
elements into k
contiguous parts by choosing k-1
split points from n-1
possible positions.
Auxiliary Space: O(k),
due to the maximum recursion depth being k
, with each call using constant space.
[Expected Approach] Using Binary Search
The approach uses Binary Search to find the minimum possible value for the maximum subarray sum when the array is split into k subarrays. The binary search range is between the maximum element of the array (since no subarray can have a sum less than the largest element) and the total sum of the array (which is the maximum sum when the entire array is considered as a single subarray). For each midpoint value, we check if it's possible to split the array into k or fewer subarrays such that no subarray's sum exceeds the midpoint. If it is possible, we attempt to minimize the maximum sum by narrowing the search range to smaller values.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool check(int mid, vector<int> &arr, int k) {
int n = arr.size();
int count = 0;
int sum = 0;
for (int i = 0; i < n; i++) {
// If individual element is greater
// maximum possible sum
if (arr[i] > mid)
return false;
// Increase sum of current sub - arr
sum += arr[i];
// If the sum is greater than
// mid increase count
if (sum > mid) {
count++;
sum = arr[i];
}
}
count++;
if (count <= k)
return true;
return false;
}
int splitArray(vector<int> &arr, int k) {
int n = arr.size();
int max = *max_element(arr.begin(), arr.end());
// Max subarr sum, considering subarr of length 1
int start = max;
// Max subarr sum, considering subarr of length n
int end = 0;
for (int i = 0; i < n; i++) {
end += arr[i];
}
// ans stores possible
// maximum sub arr sum
int ans = 0;
while (start <= end) {
int mid = (start + end) / 2;
// If mid is possible solution
// Put ans = mid;
if (check(mid, arr, k)) {
ans = mid;
end = mid - 1;
}
else {
start = mid + 1;
}
}
return ans;
}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 3;
cout << splitArray(arr, k);
}
import java.util.*;
class GfG {
static boolean check(int mid, int[] arr, int k) {
int n = arr.length;
int count = 0;
int sum = 0;
for (int i = 0; i < n; i++) {
// If individual element is greater
// maximum possible sum
if (arr[i] > mid)
return false;
// Increase sum of current sub - arr
sum += arr[i];
// If the sum is greater than
// mid increase count
if (sum > mid) {
count++;
sum = arr[i];
}
}
count++;
return count <= k;
}
static int splitArray(int[] arr, int k) {
int n = arr.length;
int max = Arrays.stream(arr).max().getAsInt();
// Max subarr sum, considering subarr of length 1
int start = max;
// Max subarr sum, considering subarr of length n
int end = 0;
for (int value : arr) {
end += value;
}
// ans stores possible
// maximum sub arr sum
int ans = 0;
while (start <= end) {
int mid = (start + end) / 2;
// If mid is possible solution
// Put ans = mid;
if (check(mid, arr, k)) {
ans = mid;
end = mid - 1;
}
else {
start = mid + 1;
}
}
return ans;
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 3;
System.out.println(splitArray(arr, k));
}
}
def check(mid, arr, k):
n = len(arr)
count = 0
total = 0
for num in arr:
# If individual element is greater
# maximum possible sum
if num > mid:
return False
# Increase sum of current sub-array
total += num
# If the sum is greater than mid, increase count
if total > mid:
count += 1
total = num
count += 1
return count <= k
def splitArray(arr, k):
n = len(arr)
start = max(arr)
end = sum(arr)
# ans stores possible maximum subarray sum
ans = 0
while start <= end:
mid = (start + end) // 2
# If mid is possible solution, set ans = mid
if check(mid, arr, k):
ans = mid
end = mid - 1
else:
start = mid + 1
return ans
arr = [1, 2, 3, 4]
k = 3
print(splitArray(arr, k))
using System;
using System.Linq;
class GfG {
static bool Check(int mid, int[] arr, int k) {
int n = arr.Length;
int count = 0;
int sum = 0;
for (int i = 0; i < n; i++) {
// If individual element is greater
// maximum possible sum
if (arr[i] > mid)
return false;
// Increase sum of current sub-array
sum += arr[i];
// If the sum is greater than mid, increase
// count
if (sum > mid) {
count++;
sum = arr[i];
}
}
count++;
return count <= k;
}
static int SplitArray(int[] arr, int k) {
int n = arr.Length;
int start = arr.Max();
int end = 0;
foreach(int num in arr) { end += num; }
// ans stores possible maximum subarray sum
int ans = 0;
while (start <= end) {
int mid = (start + end) / 2;
// If mid is possible solution, set ans = mid
if (Check(mid, arr, k)) {
ans = mid;
end = mid - 1;
}
else {
start = mid + 1;
}
}
return ans;
}
static void Main() {
int[] arr = { 1, 2, 3, 4 };
int k = 3;
Console.WriteLine(SplitArray(arr, k));
}
}
function check(mid, arr, k) {
const n = arr.length;
let count = 0;
let sum = 0;
for (let i = 0; i < n; i++) {
// If individual element is greater
// maximum possible sum
if (arr[i] > mid) {
return false;
}
// Increase sum of current sub-array
sum += arr[i];
// If the sum is greater than mid, increase count
if (sum > mid) {
count++;
sum = arr[i];
}
}
count++;
return count <= k;
}
function splitArray(arr, k) {
const n = arr.length;
let start
= Math.max(...arr);
let end
= arr.reduce((a, b) => a + b,
0);
// ans stores possible maximum subarray sum
let ans = 0;
while (start <= end) {
const mid = Math.floor((start + end) / 2);
// If mid is possible solution, set ans = mid
if (check(mid, arr, k)) {
ans = mid;
end = mid - 1;
}
else {
start = mid + 1;
}
}
return ans;
}
// Driver Code
const arr = [ 1, 2, 3, 4 ];
const k = 3;
console.log(splitArray(arr, k));
Output
4
Time Complexity: O(n * log(sum)),
where n
is the array size and sum
is the total sum of the array, due to binary search from max(arr)
to sum(arr)
and linear checking in each iteration.
Auxiliary Space: O(1),
uses constant extra space regardless of input size (excluding input and output storage).