Problem Description
Given an integer array nums
and an integer k
, split nums
into k
non-empty subarrays such that the largest sum of any subarray is minimized.
Return the minimized largest sum of the split.
A subarray is a contiguous part of the array.
Example 1:
Input: nums = [7,2,5,10,8], k = 2 Output: 18 Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [7,2,5] and [10,8], where the largest sum among the two subarrays is only 18.
Example 2:
Input: nums = [1,2,3,4,5], k = 2 Output: 9 Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [1,2,3] and [4,5], where the largest sum among the two subarrays is only 9.
Constraints:
1 <= nums.length <= 1000
0 <= nums[i] <= 106
1 <= k <= min(50, nums.length)
Difficulty: Hard
Tags: array, binary search, dynamic programming, greedy, prefix sum
Rating: 97.72%
Solution Approach
The solution uses binary search to find the optimal maximum subarray sum
-
Search Space:
- The minimum possible value for the largest sum is
max(nums)
(when k equals the array length) - The maximum possible value is
sum(nums)
(when k = 1)
- The minimum possible value for the largest sum is
-
Decision Problem:
- Instead of directly finding the minimum largest sum, we can solve an easier decision problem
- For a given target sum S, can we split the array into k or fewer subarrays such that no subarray sum exceeds S?
-
Binary Search Application:
- If we can split the array with a certain maximum sum, we might be able to do better (try a smaller sum)
- If we can’t split the array with a certain maximum sum, we need to increase our target
Implementation
Here’s the solution with detailed explanations:
class Solution:
def splitArray(self, nums: List[int], k: int) -> int:
def canSplit(max_sum):
# This helper function checks if we can split the array into k or fewer
# subarrays while keeping each subarray's sum <= max_sum
count = 1 # Start with one subarray
current_sum = 0
for num in nums:
# If adding current number exceeds max_sum,
# we need to start a new subarray
if current_sum + num > max_sum:
count += 1
current_sum = num
else:
current_sum += num
return count <= k # Can we achieve k or fewer splits?
# Initialize binary search boundaries
left = max(nums) # Minimum possible maximum sum
right = sum(nums) # Maximum possible maximum sum
result = right # Initialize with worst case
# Binary search for the smallest valid maximum sum
while left <= right:
mid = left + (right - left) // 2
if canSplit(mid):
# If we can split with this max_sum, try a smaller value
result = mid
right = mid - 1
else:
# If we can't split with this max_sum, try a larger value
left = mid + 1
return result
Algorithm Steps
-
Binary Search Setup:
- The search space is from
max(nums)
tosum(nums)
- If k = 1, the answer is
sum(nums)
- If k = length of nums, the answer is
max(nums)
- Any valid answer must lie within this range
- The search space is from
-
The Helper Function (
canSplit
):- Takes a target maximum sum as input
- Greedily tries to form subarrays
- Keeps track of current subarray sum
- Starts a new subarray when adding the next number would exceed the target
- Returns whether we can achieve the split with k or fewer subarrays
-
Binary Search Process:
- For each middle point, we check if it’s possible to split the array
- If possible, we try a smaller maximum sum
- If not possible, we try a larger maximum sum
- We keep track of the smallest valid maximum sum found
Time and Space Complexity
-
Time Complexity:
- Binary search range:
sum(nums) - max(nums)
- Each
canSplit
call takes O(n) time
- Binary search range:
-
Space Complexity:
- Only uses a constant amount of extra space
Example Walkthrough
Let’s walk through Example 1: nums = [7,2,5,10,8], k = 2
-
Initial setup:
left = max(nums) = 10
right = sum(nums) = 32
-
First iteration:
mid = 21
- Can split: [7,2,5] [10,8]
- Update
right = 20
-
Second iteration:
mid = 15
- Cannot split (would need 3 subarrays)
- Update
left = 16
-
Process continues until we find the optimal value of 18