</> MAANG.io
coding interview · 301

Advanced

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/95 solved 0% complete

Binary-Search Partition

What is this?

Both problems cut an array into k contiguous pieces and ask about the extreme piece — the largest in one, the smallest in the other. Neither is a DP table. You guess the answer, check it with a linear greedy, and let binary search find the boundary where feasible turns infeasible.

Learning them as a pair is the point: they are the same algorithm reflected, and the two reflections differ in three specific places.

flowchart TD A["cut into k contiguous pieces"] --> B["which extreme?"] B --> C["minimise the LARGEST piece"] B --> D["maximise the SMALLEST piece"] C --> C1["can we fit within cap?
fill greedily, count pieces ≤ k"] C1 --> C2["search range [max, sum]"] C2 --> C3["mid = (lo+hi)//2, feasible → hi = mid"] D --> D1["can every piece reach m?
cut as soon as m is met"] D1 --> D2["search range [min, sum]"] D2 --> D3["mid = (lo+hi+1)//2, feasible → lo = mid"]

💡 Fun fact: the + 1 in one midpoint and not the other is not a stylistic choice — it is what stops an infinite loop. When feasibility means "move lo up", lo and hi eventually sit adjacent, mid rounds down to lo, and lo = mid changes nothing: the loop spins forever. Biasing the midpoint upward with (lo + hi + 1) // 2 fixes it. The rule is simple and worth memorising: the branch that assigns mid to lo needs the + 1; the branch that assigns mid to hi must not have it.

🔓 The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: when the question is "the best possible worst piece", guess the answer and verify it greedily. The verification is linear and obvious; the search around it is five lines.


1. Minimise the largest

def feasible(cap):
    pieces, cur = 1, 0
    for x in nums:
        if cur + x > cap: pieces += 1; cur = x     # start a new piece
        else:             cur += x
    return pieces <= k

lo, hi = max(nums), sum(nums)                      # the bounds are not arbitrary
while lo < hi:
    mid = (lo + hi) // 2
    if feasible(mid): hi = mid
    else:             lo = mid + 1
return lo

The bounds carry an argument each. max(nums) is the floor because a single element can never be split, and sum(nums) is the ceiling because one piece holding everything always works. Between them, feasibility is monotonic — if a cap works, every larger cap works — which is the only condition binary search needs.

2. Maximise the smallest

def feasible(m):
    pieces, cur = 0, 0
    for s in sweetness:
        cur += s
        if cur >= m: pieces += 1; cur = 0          # cut as soon as the piece qualifies
    return pieces >= k + 1                         # k friends → k+1 pieces

lo, hi = min(sweetness), sum(sweetness)
while lo < hi:
    mid = (lo + hi + 1) // 2                       # ← the +1
    if feasible(mid): lo = mid
    else:             hi = mid - 1
return lo

Three differences from the first, and all three are forced. The greedy cuts as soon as the piece qualifies rather than as late as possible. The comparison is >= against a count that must be at least k + 1, because k friends means the chocolate is divided into k + 1 parts and one of them is yours. And the midpoint carries the + 1.

Leftover pieces at the end are simply discarded in the feasibility check — they can always be attached to the last piece, which only makes it larger and therefore still valid.


3. A 30-second worked example (minimise the largest)

nums = [7, 2, 5, 10, 8], k = 2. Search range is [10, 32].

mid = 21   fill: 7+2+5 = 14, +10 = 24 > 21 → new piece
                 10+8 = 18                        2 pieces ≤ 2  ✓   hi = 21
mid = 15   fill: 7+2+5 = 14, +10 = 24 > 15 → new piece
                 10, +8 = 18 > 15 → new piece     3 pieces > 2  ✗   lo = 16
mid = 18   fill: 7+2+5 = 14, +10 > 18 → new piece
                 10+8 = 18                        2 pieces ≤ 2  ✓   hi = 18
mid = 17   fill: 7+2 = 9, +5 = 14, +10 > 17 → new
                 10, +8 = 18 > 17 → new           3 pieces > 2  ✗   lo = 18

lo == hi == 18       → split as [7,2,5] and [10,8]

Note that 18 is feasible and 17 is not — that single boundary is the answer, and the greedy never had to be clever. It just fills each piece until adding the next element would overflow, which is optimal for this question precisely because pieces must be contiguous.


4. Where you'll actually meet this

  • Capacity planning. Fewest shipments, machines or workers to finish under a deadline is this exact search.
  • Load balancing. Partitioning ordered work across servers so the busiest is as idle as possible.
  • Pagination and layout. Splitting content into pages whose heaviest page is minimal.
  • Resource fairness. Maximising the smallest share is the standard formulation of a fair split.
  • Rate and quota design. Finding the smallest limit under which every consumer still fits.

5. Problems in this chapter

▶ Divide Chocolate

Cut into k + 1 contiguous pieces maximising the smallest total. Binary-search the minimum, cutting as soon as each piece qualifies.
Pattern: maximise the minimum. Target: O(n log(sum)) time, O(1) space.

▶ Split Array Largest Sum

Split into k contiguous subarrays minimising the largest sum. Binary-search the cap, filling each piece until it would overflow.
Pattern: minimise the maximum. Target: O(n log(sum)) time, O(1) space.


6. Common pitfalls 🚫

  • Dropping the + 1 in the midpoint of the maximise-the-minimum search, which loops forever.
  • Adding it to the other one, which overshoots and returns an infeasible answer.
  • Counting k pieces in Divide Chocolate. k friends means k + 1 pieces.
  • Starting the search at 0 or 1. max(nums) and min(sweetness) are provable floors and save iterations.
  • Cutting greedily in the wrong direction. Minimising the maximum fills as much as possible; maximising the minimum cuts as soon as it can.
  • Not stating why feasibility is monotonic. Without it, binary search is unjustified even when it works.
  • Reaching for a DP table. It exists, it is O(n²k), and it is the slower answer.

7. Key takeaways

  1. Guess the answer and verify it — this whole family avoids a table entirely.
  2. Monotonic feasibility is the licence to binary-search. Say why it holds.
  3. The two directions are mirrors, and the three differences between them are all forced.
  4. + 1 belongs to the branch that assigns mid to lo — nowhere else.
  5. Bounds are provable, not arbitrary. A single element cannot be split; everything in one piece always works.
  6. The greedy check is linear and boring, which is exactly what you want inside a search.
  7. Why interviewers like it: the pair tests whether you learned a template or a technique, and the + 1 shows instantly which.

Order: Divide Chocolate → Split Array Largest Sum.

Split Array Largest Sum

Core idea: Stop searching for where to cut and start searching for how big the worst piece is allowed to be. Guess a cap C on the largest subarray sum. There is a one-pass greedy test for "can I split nums into at most m contiguous pieces with no piece exceeding C?" — and that test is monotonic: if a cap works, every larger cap works too. So binary-search the smallest cap that passes. The answer lives in [max(nums), sum(nums)], and each feasibility check is O(n), giving O(n log(sum)).

The problem (LeetCode 410)

You are given an integer array nums and an integer m. Split nums into m non-empty contiguous subarrays (you may not reorder elements). Among all ways to split it, minimize the largest sum of any one subarray. Return that minimized largest sum.

Constraints: 1 <= nums.length <= 1000, 0 <= nums[i] <= 10^6, 1 <= m <= min(50, nums.length).

A fresh way to picture it

Forget arrays. You run a parcel depot with a conveyor of boxes lined up in a fixed order, and you have m delivery trucks. Boxes must be loaded in order — truck 1 takes a contiguous front chunk, truck 2 the next chunk, and so on — and every truck must carry at least one box. Each truck's load is the sum of its box weights. You want to assign the chunks so that the heaviest-loaded truck is as light as possible, because that truck is your bottleneck for the day. "Minimize the largest subarray sum" is exactly "minimize the heaviest truck."

nums m best split largest subarray sum
[7,2,5,10,8] 2 [7,2,5] | [10,8] 18
[1,2,3,4,5] 2 [1,2,3] | [4,5] 9
[1,4,4] 3 [1] | [4] | [4] 4
[2,3,1,2,4,3] 3 [2,3] | [1,2] | [4,3] wait → see below 6

(For [2,3,1,2,4,3], m=3 the optimal is [2,3,1] \| [2,4] \| [3] with sums 6,6,3 → largest 6; the example just shows there are several splits that tie at 6.)

Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.