Given an integer array and an integer k, your task is to divide the array into k contiguous, non-empty subarrays. You must perform this Split Array Largest Sum operation in such a way that the largest sum among these k subarrays is minimized. Your function needs to return this minimized largest sum.
Example 1
Input: nums = [7, 2, 5, 10, 8], k = 2
Output: 18
Explanation: The best way to divide the array into 2 subarrays is [7, 2, 5] and [10, 8]. The sum of the first subarray is 14, and the sum of the second is 18. The largest sum between the two is 18. Any other division would result in a largest sum greater than 18.
Example 2
Input: nums = [1, 2, 3, 4, 5], k = 2
Output: 9
Explanation: The best way to divide the array is [1, 2, 3] and [4, 5]. The sums are 6 and 9. The largest sum is 9, which is the absolute minimum possible for 2 contiguous subarrays.
Brute Force Approach
The smallest possible answer cannot be less than the largest element in the array. Every element must belong to some subarray, so the subarray containing the largest element will have at least that much sum.
The largest answer we ever need is the total sum of the array. That happens when all elements are kept in one subarray. So the answer must lie between: max(nums) and sum(nums)
The direct idea is to try every possible value in this range as the allowed maximum subarray sum. For each value, greedily scan the array and count how many subarrays are needed if no subarray is allowed to cross that value. If the needed number of subarrays is at most k, that value is possible. The first possible value is the answer because values are checked from small to large.
Algorithm
Find the largest element and the total sum of the array. The largest element becomes the smallest answer worth trying, and the total sum becomes the largest answer worth trying.
Try every possible maximum sum from the lower bound to the upper bound. This is done in increasing order so the first valid value is guaranteed to be minimum.
For each chosen value, scan the array from left to right and keep the sum of the current subarray. The scan must stay left to right because subarrays must be contiguous.
If adding the next number would cross the chosen maximum sum, start a new subarray from that number. This gives the minimum number of subarrays needed for that chosen limit.
If the number of subarrays needed is at most
k, return the chosen value. Even if fewer thanksubarrays are formed, they can be split further without increasing the largest sum.
Dry Run
Split Array Largest Sum Brute Dry Run
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: /* Counts how many subarrays are needed when no subarray sum can exceed maxAllowed. */ int countSubarrays(vector<int>& nums, int maxAllowed) { // At least one subarray is needed when nums is not empty. int subarrays = 1; // This stores the sum of the current subarray. int currentSum = 0; for (int num : nums) { // Start a new subarray when adding num // would cross the allowed maximum sum. if (currentSum + num > maxAllowed) { subarrays++; // The current number begins the next subarray. currentSum = num; } else { // The current number still fits, // so keep it in the same subarray. currentSum += num; } } return subarrays; } /* Returns the minimized largest subarray sum by trying every possible answer. */ int splitArray(vector<int>& nums, int k) { // The answer cannot be smaller than the largest element. int low = *max_element(nums.begin(), nums.end()); // The answer never needs to be larger than the total sum. int high = accumulate(nums.begin(), nums.end(), 0); for (int maxAllowed = low; maxAllowed <= high; maxAllowed++) { // The first valid value is minimum because // values are checked from small to large. if (countSubarrays(nums, maxAllowed) <= k) { return maxAllowed; } } return high; }};// Driver code startsint main() { vector<int> nums = {7, 2, 5, 10, 8}; int k = 2; Solution obj; cout << obj.splitArray(nums, k) << endl; return 0;}Complexity Analysis
Time Complexity: O(N x (Sum - Max + 1)), N is the length of nums array, because every possible maximum sum may scan the whole array.
Space Complexity: O(1), because constant space is used.
Optimal Approach
The important observation is how the chosen maximum allowed sum affects the number of subarrays. If the allowed maximum sum is small, each subarray can hold only a little, so more subarrays are needed. If the allowed maximum sum is large, each subarray can hold more elements, so fewer subarrays are needed. That creates a monotonic pattern:
not possible, not possible, possible, possible, possible
The answer is the first possible value. For a fixed value, the best way to count subarrays is greedy. Keep adding elements to the current subarray while the sum stays within the limit. Start a new subarray only when the next element would overflow the limit.
This greedy check uses the fewest subarrays for that limit because it keeps every subarray as full as possible before cutting. If even this greedy method needs more than k subarrays, no other split can make that limit work.
One small doubt often appears here: the problem asks for exactly k subarrays, but the check uses at most k. This works because if a limit can make fewer than k subarrays, any subarray with more than one element can be split further. Splitting only makes sums smaller or equal, so the largest sum does not increase.
Algorithm
Set the search range from
max(nums)tosum(nums). The lower bound is needed because every element must fit inside some subarray, and the upper bound is enough because the whole array can be one subarray.Pick the middle value of the range. Treat this value as the maximum allowed subarray sum, not as an array index.
Check whether the array can be split with this limit. Scan from left to right, add numbers to the current subarray, and start a new subarray only when adding the next number would cross the limit.
If the check uses at most
ksubarrays, the limit is valid. Store it as a possible answer and search the left half because a smaller valid limit may exist.If the check uses more than
ksubarrays, the limit is too small. Search the right half because larger limits allow subarrays to carry more sum.When the search ends, return the smallest valid value found.
Key Points
Checking
<= kis correct even though the final split needs exactlyksubarrays.k == 1makes the answer equal tosum(nums).k == nums.lengthmakes the answer equal tomax(nums).
Dry Run
Split array Largest Sum Optimal Dry Run
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: /* Checks whether nums can be split into at most k subarrays when no subarray sum can exceed maxAllowed. */ bool canSplit(vector<int>& nums, int k, int maxAllowed) { // At least one subarray is needed when nums is not empty. int subarrays = 1; // This stores the sum of the current subarray. int currentSum = 0; for (int num : nums) { // Start a new subarray when adding num // would make the current sum too large. if (currentSum + num > maxAllowed) { subarrays++; // The current number begins the new subarray. currentSum = num; // More than k subarrays means this limit is too small. if (subarrays > k) { return false; } } else { // The number fits in the current subarray, // so no new split is needed here. currentSum += num; } } // The limit works when at most k subarrays are needed. return subarrays <= k; } /* Returns the minimized largest subarray sum using binary search on possible answer values. */ int splitArray(vector<int>& nums, int k) { // The answer cannot be smaller than the largest element. int low = *max_element(nums.begin(), nums.end()); // The answer never needs to be larger than the total sum. int high = accumulate(nums.begin(), nums.end(), 0); // This stores the smallest valid limit found so far. int answer = high; while (low <= high) { // mid is the maximum subarray sum being tested. int mid = low + (high - low) / 2; // If mid works, try to find a smaller valid limit. if (canSplit(nums, k, mid)) { answer = mid; high = mid - 1; } else { // If mid fails, a larger limit is required. low = mid + 1; } } return answer; }};// Driver code startsint main() { vector<int> nums = {7, 2, 5, 10, 8}; int k = 2; Solution obj; cout << obj.splitArray(nums, k) << endl; return 0;}Complexity Analysis
Time Complexity: O(N x log2(Sum - Max + 1)), N is the length of nums array, because every binary-search check scans the array once. Binary search has a range of Sum-Max+1 giving complexity of log2(Sum-Max+1).
Space Complexity: O(1), because constant space is used.
Interview follow-up Questions
Contiguous means the elements inside the subarray must sit directly next to each other in the original array without any gaps. You cannot skip elements or reorder them when forming a subarray.
Be the first to add a comment.