Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Coding · Binary search

The pattern: minimise the maximum

Lesson · 12 min read · Binary search · Pattern: minimise the maximum

The smallest capacity, speed or time that still gets the job done: guess a value, check it with one greedy pass, and search for the smallest that passes.

What the pattern is

The question asks for the smallest value of some limit (a capacity, a rate, a deadline, the largest part of a split) under which a task can still be completed. Constructing the best arrangement directly is hard. Checking whether a given limit is enough is easy, usually one greedy pass. And a larger limit is never worse than a smaller one. That is a first-true search over limits, with the greedy pass as the test.

Minimise the maximum

solve(input)

  1. lo = the smallest limit that could possibly work; hi = one that certainly does.
  2. feasible(c): simulate the task under limit c greedily; return whether it finishes within the budget.
  3. First-true search: while lo < hi, mid = lo + (hi − lo) / 2; if feasible(mid), hi = mid, else lo = mid + 1.
  4. Return lo.

How to recognise it

  • "The minimum capacity / speed / time / largest sum such that …", with a budget: within D days, in h hours, into k parts, by the deadline.
  • The answer is a number from a range you can bound, not a choice among the elements.
  • You can say why it is monotone: anything that can be done with limit c can be done with limit c + 1.
  • Constraints like n up to 10⁵ and values up to 10⁹: the intended cost is O(n log(range)).

Why the greedy check is right

In the shipping problem the check loads each day with packages until the next one would overflow, then starts a new day. No other loading uses fewer days: whatever an optimal loading puts on day one, the greedy loading puts at least as many packages there, and the same argument repeats day by day. This "stay ahead" argument is the proof to give for most checks in this module. Say it in one sentence; the interviewer wants to hear that you know a check needs one.

Worked example

The smallest ship capacity for six packages in three days.

Each test runs the greedy split for one capacity.

  1. Step 1 / 7
    lo 7hi 27
    lo7hi27

    Packages 4, 7, 2, 6, 5, 3 in order, 3 days. No capacity below the heaviest package, 7, can work, and the total, 27, ships everything in one day.

  2. Step 2 / 7
    lo 7hi 27mid 17 ✓
    lo7mid17hi27

    Capacity 17: [4, 7, 2] [6, 5, 3], 2 days. 17 works, so the answer is 17 or smaller: hi = 17.

  3. Step 3 / 7
    lo 7hi 1727mid 12 ✓
    lo7mid12hi17

    Capacity 12: [4, 7] [2, 6] [5, 3], 3 days. 12 works, so the answer is 12 or smaller: hi = 12.

  4. Step 4 / 7
    lo 7hi 1227mid 9 ✗
    lo7mid9hi12

    Capacity 9: [4] [7, 2] [6] [5, 3], 4 days. 9 fails, so does everything below it: lo = 10.

  5. Step 5 / 7
    7lo 10hi 1227mid 11 ✓
    lo10mid11hi12

    Capacity 11: [4, 7] [2, 6] [5, 3], 3 days. 11 works, so the answer is 11 or smaller: hi = 11.

  6. Step 6 / 7
    7lo 1027mid 10 ✗
    lo10mid10hi11

    Capacity 10: [4] [7, 2] [6] [5, 3], 4 days. 10 fails, so does everything below it: lo = 11.

  7. Step 7 / 7
    727answer 11
    answer11

    The smallest capacity is 11: [4, 7] [2, 6] [5, 3]. Capacity 10 needs 4 days.

TimeO(n log(sum − max))
SpaceO(1)

One O(n) pass per test, and about log₂ of the range of capacities tests.

The problems, side by side

Every problem in this module is the same loop. What changes is the range and the check.

ProblemRangefeasible(x)
Smallest ship capacitymax weight to total weightgreedy split uses at most D days
Split to minimise the largest partmax element to totalgreedy split uses at most k parts
Slowest rate that clears every queue1 to the largest queueΣ ⌈pile / x⌉ ≤ h
First day enough flowers are openearliest to latest bloom dayruns of open flowers give at least m bouquets of k; −1 if m × k > n
Earliest time a fleet completes its trips1 to min(time) × totalTripsΣ ⌊x / time[i]⌋ ≥ totalTrips
Least time to repair every car1 to min(rank) × cars²Σ ⌊√(x / rank)⌋ ≥ cars

Two of these overflow 32-bit integers easily: min(time) × totalTrips, and rank × cars². Use 64-bit arithmetic for the bounds and inside the check, and compute ⌊√(x / rank)⌋ with an integer square root rather than a floating-point one near large values.

Problems in this module