DSA sheet · Binary Search · Binary search on answer (min of max)

Split Array Largest Sum

Cut an array into k continuous pieces so that the heaviest piece is as light as possible. The teacher calls this the last question of the binary-search-on-answer pattern, and she says it's Allocate Minimum Pages with a new story: books become numbers and students become pieces. Her video is short and keeps pointing back to that problem, so this page writes out the full reasoning here, so it can be read on its own. She lists all the splits by hand, picks low and high, shows the brute force (try every possible largest sum, TLE), and then binary searches.

Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember

Part 0 · Before starting

Normal binary search in one paragraph

Binary search needs a sorted line where a yes/no question flips only once. Keep low and high, test mid, drop the half that can't hold the answer, and repeat. About log₂(size) steps.

How to compute mid, and why that form

the safe way to find the middle
mid = low + (high - low) // 2

It's the same number as (low + high) // 2. In Java/C++, low + high can overflow the int limit (about 2.1 × 10⁹) when both are large. Here high is a sum that can reach 10³ × 10⁶ = 10⁹, so two such values added together can cross the limit. The subtraction form never overflows. Python integers have no size limit, so in Python this is just a good habit.

The pattern: binary search on the answer

  1. Answer range: the smallest and the largest value the answer could possibly be.
  2. Yes/no check: "if no piece may have a sum above X, can I cut the array into at most k pieces?"
  3. Monotonic: a bigger X never needs more pieces, so the answers go no, no, …, yes, yes. One flip → binary search.

The tell-tale sign: the answer (18 in the example) is a sum, not an element you can find in the array. We search a range of possible sums.

Minimise the maximum vs maximise the minimum

The teacher points out the two directions in one line: when the question says minimise the maximum, the lowest sensible value is a maximum (the biggest element); when it says maximise the minimum, you push the minimum up. And the direction of the search flips with it:

minimise the maximum (this page, Allocate Pages)maximise the minimum (Aggressive Cows, Magnetic Force)
candidate X means"no piece may be heavier than X""every two items at least X apart"
lineN N N Y Y YY Y Y N N N
we wantthe first yesthe last yes
check(mid) yessave, high = mid - 1 (go left)save, low = mid + 1 (go right)
check(mid) nolow = mid + 1high = mid - 1

Part A · Brute force: try every largest sum one by one

LeetCode 410

1The question in simple words

You get an array nums and a number k. Cut the array into exactly k pieces. Each piece must be a subarray: a continuous stretch, never empty, and you can't take numbers from different places. For each way of cutting, find the piece with the largest sum. Choose the cutting where that largest sum is smallest, and return it.

Example: nums = [7, 2, 5, 10, 8], k = 2 → answer 18.

index01234
value725108

2What the constraints tell us

3Intuition

Instead of trying all cuts, fix a cap X and ask: "if no piece may sum to more than X, how many pieces do I need?" Walk left to right, keep adding numbers to the current piece, and when the next number would push it over X, start a new piece with that number. If the count is ≤ k, X is possible. Try X from small to big. The first possible X is the answer.

4Building the logic from examples

Step 1: all the ways to cut [7, 2, 5, 10, 8] into 2 pieces

piece 1piece 2sumslargest
72, 5, 10, 87, 2525
7, 25, 10, 89, 2323
7, 2, 510, 814, 1818 ← smallest
7, 2, 5, 10824, 824

The teacher gets the second sums quickly from the total: 7 + 2 + 5 + 10 + 8 = 32, so the other piece is 32 minus the first piece (32 − 7 = 25, 32 − 9 = 23, …). The largest in each row is 25, 23, 18, 24, and the smallest of those is 18, matching the expected output. "Minimise the largest sum."

Step 2: high = the total sum

k can be 1. Then the whole array is one piece and the answer is the total, 32. With more pieces the largest piece can only shrink. So nothing above the total is ever needed. high = sum(nums).

Step 3: low = the largest element

Looking at the four splits, you might guess the smallest number, 7, or perhaps 8. The teacher shows why that's wrong: imagine the array ended with …, 8, 10. You could put 10 alone and everything else in the other piece, but the piece containing 10 still weighs at least 10. Every number has to go in some piece, and a piece is at least as heavy as its biggest number. So the answer can never be below max(nums) = 10. low = max(nums).

Doubt: why not low = 7 (the smallest element)?
→ Any cap below 10 can't hold the number 10 in any piece, so the check would always say no for 7, 8 and 9. Starting at 7 isn't wrong, it just wastes checks. And when k = n (one number per piece), the answer is exactly max(nums), so the max really can be the answer.

The answer lives on 10 … 32.

Step 4: the check, tried at a few caps

The yes/no line

cap X10…1314…171819…3132
pieces4…43…322…21
≤ k?N…NN…NYY…YYanswer = the first Y = 18
Doubt: why is "fewer than k pieces" a yes? We need exactly k.
→ If everything fits in fewer pieces under cap X, you can split any piece with 2+ numbers into two. Both halves are still ≤ X. Keep doing that until you have exactly k pieces. Since k ≤ n, there are always enough numbers to do this. So "count ≤ k" is the right test, and it's what makes the line flip only once.

The check function, piece by piece (same as Allocate Pages)

Doubt: why start count at 1?
→ We only count a piece when a new one opens. The very first piece is open before the loop starts, so it's counted up front. Otherwise the last piece would be missed.

The brute-force loop

The teacher's version: set answer = high first. Loop X from low to high. On the first X where the check says yes, save it and break (or return it right away). Going upward, the first yes is the smallest. If nothing broke the loop, answer is still the total sum, which is always valid.

5Approach steps

  1. low = max(nums), high = sum(nums), answer = high.
  2. For X = low … high: if canSplit(nums, k, X) → answer = X, break.
  3. Return answer.

6Code (Python)

Split Array Largest Sum, brute force (TLE on big inputs)
class Solution:
    def splitArray(self, nums, k):
        low = max(nums)
        high = sum(nums)
        answer = high
        for max_sum in range(low, high + 1):
            if self.canSplit(nums, k, max_sum):
                answer = max_sum        # first yes = smallest largest-sum
                break
        return answer

    def canSplit(self, nums, k, max_sum):
        count = 1
        current = 0
        for num in nums:
            if current + num <= max_sum:
                current += num          # stays in this piece
            else:
                count += 1              # open a new piece...
                current = num           # ...starting with this number
        return count <= k

7Code line by line

linewhat it means
low = max(nums)Some piece must hold the biggest number.
high = sum(nums)The k = 1 answer, the biggest that could ever be needed.
answer = highA safe fallback, since the total sum always works.
for max_sum in range(low, high + 1):Try each cap from small to big.
answer = max_sum breakThe first cap that works is the smallest one.
count = 1 current = 0Piece 1 is open and empty.
if current + num <= max_sum:Does this number still fit in the current piece?
count += 1 current = numNo → the next piece begins with this number.
return count <= kAt most k pieces were needed → this cap is possible.

8Dry run (hand table)

nums = [7, 2, 5, 10, 8], k = 2. low = 10, high = 32.

cappiecescountcheck
10 … 13[7,2] [5] [10] [8]4no
14 … 17[7,2,5] [10] [8]3no
18[7,2,5] [10,8]2yes → answer 18, break

9 caps tried (10 through 18). Return 18 ✓.

9Complexity & remember

Doubt (a correction): is the outer loop really at most 10⁶?
→ It goes up to sum(nums), and the sum can be about 10³ × 10⁶ = 10⁹. So the worst case is even bigger than her 10⁹ estimate. Either way it's TLE, so we need to optimise.
Remember brute forcelow = max, high = sum. Greedy count of pieces under the cap. The first cap with count ≤ k is the answer. Too slow because the cap range is huge.

Part B · Optimal: binary search on the largest sum

1The question

Same problem, same range, same canSplit. The teacher's only change: replace the for loop with a binary-search while loop.

2Constraints

Same. The cap range is at most about 10⁹ values wide, so about 30 checks of 10³ steps each.

3Intuition

The caps 10 … 32 form a sorted line that reads N … N Y … Y, and we want the first Y (we're minimising).

4Building the logic

Doubt 1: why not just return mid on yes?
→ A yes only proves mid is enough, not that it's the smallest. Save it and keep checking to the left. The last value saved is the first Y.
Doubt 2: what if all numbers are 0?
→ low = high = 0. The check at 0: every 0 + 0 ≤ 0 fits in one piece → 1 ≤ k → yes. Answer 0. Nothing breaks.

The range shrinking

cap10…15161718…21…32
step 110…15161718…21…32mid 21 → yes → save, drop 21…32
step 210…15161718…21…32mid 15 → no → drop 10…15
step 310…15161718…21…32mid 18 → yes → save 18, drop 18…20
steps 4–510…15161718…21…3216 no, 17 no → low passes high → answer 18

5Approach steps

  1. low = max(nums), high = sum(nums), answer = high.
  2. While low <= high: mid = low + (high - low) // 2.
  3. canSplit(mid) → answer = mid, high = mid - 1. Else → low = mid + 1.
  4. Return answer.

6Code (Python)

Split Array Largest Sum, binary search on the answer
class Solution:
    def splitArray(self, nums, k):
        low = max(nums)
        high = sum(nums)
        answer = high
        while low <= high:
            mid = low + (high - low) // 2      # candidate largest sum
            if self.canSplit(nums, k, mid):
                answer = mid                   # enough: save it...
                high = mid - 1                 # ...and try a smaller cap
            else:
                low = mid + 1                  # too many pieces: bigger cap
        return answer

    def canSplit(self, nums, k, max_sum):
        count = 1
        current = 0
        for num in nums:
            if current + num <= max_sum:
                current += num
            else:
                count += 1
                current = num
        return count <= k

7Code line by line

linewhat it means
while low <= high:Untested caps remain (including the case where only one is left).
mid = low + (high - low) // 2The middle cap, written the overflow-safe way.
answer = mid high = mid - 1At most k pieces are enough at cap mid. Keep it, then look for a smaller cap.
low = mid + 1mid needs more than k pieces, so the cap must be bigger.
canSplit(...)Unchanged from Part A.

8Dry run (hand table)

nums = [7, 2, 5, 10, 8], k = 2.

steplowhighmidpieces at midcheck(mid)decisionthrown away
1103221[7,2,5] [10,8]yes (2)answer = 21, high = 2021…32
2102015[7,2,5] [10] [8]no (3)low = 1610…15
3162018[7,2,5] [10,8]yes (2)answer = 18, high = 1718…20
4161716[7,2,5] [10] [8]no (3)low = 1716
5171717[7,2,5] [10] [8]no (3)low = 1817
end1817low > high → return 18 ✓

9Complexity & remember

Remember the optimal wayIdentical to Allocate Pages: low = max, high = sum, greedy piece count, yes → save and go left, no → go right.

Part C · Revision page

Allocate Minimum PagesSplit Array Largest Sum
itemsbooks (pages)numbers
groupsk studentsk subarrays
minimisethe largest group sum, with continuous groups and each one non-empty
range / check / movesidentical: low = max, high = sum, greedy count ≤ k, yes → left
impossible casek > n → −1never (k ≤ n guaranteed)
zeros allowed?no (pages ≥ 1)yes (nums[i] ≥ 0)
A · brute forceB · binary search
caps triedlow, low+1, … until the first yesabout log₂(sum − max) mids
timeO(n · (sum − max)) (TLE)O(n · log(sum − max))
minimise the maximummaximise the minimum
lineN N N Y Y YY Y Y N N N
wantfirst yeslast yes
yes →high = mid - 1low = mid + 1
If you remember only 5 lines 1. The answer is a sum, not an element → binary search on the answer.
2. low = max(nums) (it must sit in some piece), high = sum(nums) (k = 1).
3. Check: grow the current piece until the next number would cross the cap, then start a new piece with that number. count ≤ k → yes.
4. Bigger cap → fewer pieces, so there's one flip from no to yes.
5. Yes → save, high = mid − 1. No → low = mid + 1.
Mistakes to avoid ✗ low = min(nums) or low = 0 (works, but wastes checks) / low too high
✗ current = 0 on a new piece (the number gets lost)
✗ count == k instead of count <= k
✗ going right on yes (that's the cows direction)
✗ < instead of <= in the fit test (a piece exactly equal to the cap is fine)
test it yourself (paste under any of the solutions above)
s = Solution()
print(s.splitArray([7, 2, 5, 10, 8], 2))     # 18
print(s.splitArray([1, 2, 3, 4, 5], 2))      # 9
print(s.splitArray([1, 4, 4], 3))            # 4
print(s.splitArray([0, 0, 0], 1))            # 0

Based on this video: Split Array Largest Sum | Binary Search on Answer