Problem
Koko has n piles of bananas and must finish all of them before the guards return in h hours. Each hour she picks exactly one pile and eats up to k bananas โ if the pile has fewer than k, she finishes it and the hour ends. Find the minimum eating speed k (bananas per hour) that still lets her finish in time.
- Input:
piles = [3, 6, 7, 11],h = 8 - Output:
4 - Explanation: At speed 4, she needs โ3/4โ+โ6/4โ+โ7/4โ+โ11/4โ = 1+2+2+3 = 8 hours, exactly fitting within the limit.
Intuition
As Koko's speed increases, the total hours needed never goes up โ it either stays the same or decreases. This monotone property means we don't need to try every speed; we can binary search over the candidate range and efficiently home in on the minimum valid speed.
Approach 1 โ Linear Scan
Try every speed from 1 upward, computing total hours at each step, and return the first speed that fits within h hours.
Steps:
- Iterate
speedfrom1tomax(piles) - For each speed, sum
ceil(pile / speed)across all piles - Return the first speed where the total is
โค h
1import math
2
3def minEatingSpeed(piles: list[int], h: int) -> int:
4 for speed in range(1, max(piles) + 1):
5 hours_needed = sum(math.ceil(pile / speed) for pile in piles)
6 if hours_needed <= h:
7 return speed
8 return max(piles)- Time: O(n ร max(piles)) โ scans up to max(piles) candidate speeds, checking all n piles for each
- Space: O(1) โ only a constant number of variables beyond the input
Approach 2 โ Binary Search on Speed (Optimal)
Binary search over the speed range [1, max(piles)]. For each midpoint, compute whether that speed finishes in time. If it does, try slower; if not, try faster.
Steps:
- Set
low = 1,high = max(piles)(the largest pile eaten in one hour is always sufficient) - While
low < high, computemid = (low + high) // 2 - Compute
hours_needed = sum(ceil(pile / mid))across all piles - If
hours_needed <= h, mid is valid but might be too fast โ sethigh = midto preserve it as a candidate - Otherwise mid is too slow โ set
low = mid + 1to search faster speeds - Return
lowโ the smallest speed where the condition is satisfied
1import math
2
3def minEatingSpeed(piles: list[int], h: int) -> int:
4 low, high = 1, max(piles)
5
6 while low < high:
7 mid = (low + high) // 2
8 hours_needed = sum(math.ceil(pile / mid) for pile in piles)
9
10 if hours_needed <= h:
11 high = mid # mid works; try something smaller
12 else:
13 low = mid + 1 # mid is too slow; minimum valid speed is at least mid+1
14
15 return low- Time: O(n log M) where M = max(piles) โ binary search runs O(log M) rounds, each scanning all n piles
- Space: O(1) โ only a constant number of variables beyond the input
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Linear Scan | O(n ร M) | O(1) | Never โ max pile can be 10โน, making this infeasible |
| Binary Search | O(n log M) | O(1) | Always โ this is the intended approach |
Common Mistakes
- Using
pile // kinstead ofceil(pile / k): Integer division undercounts hours โ a pile of 7 at speed 5 takes 2 hours, not 1. Use(pile + k - 1) // kfor exact integer ceiling, ormath.ceil(pile / k)in Python. - Setting
high = mid - 1when hours fit: Sincemiditself may be the minimum valid speed, shrinking tomid - 1discards the answer. The correct update ishigh = midto keep the candidate in range. - Setting
high = sum(piles): Technically valid but the answer is never larger thanmax(piles)โ at that speed even the biggest pile takes just one hour. The unnecessarily wide range wastes O(log(sum) โ log(max)) rounds. - Not using
longforhoursNeededin Java/C++: At speed 1 the total hours equalssum(piles), which can reach 10โด ร 10โน = 10ยนยณ โ far beyond a 32-bit int. Accumulate into along/long long. - Returning
highinstead oflow: Because the loop exits whenlow == high, both hold the same value โ but in variants where they diverge at exit, returning the wrong one gives an off-by-one. Reasoning about which side of the invariant holds the answer avoids this.
Related Problems
capacity-to-ship-packages-within-d-daysโ identical binary search on the answer pattern with a weight-sum feasibility checksplit-array-largest-sumโ binary search on the maximum subarray sum; same monotone structuremagnetic-force-between-two-ballsโ binary search on minimum gap between placed itemsfind-first-and-last-position-of-element-in-sorted-arrayโ builds the left-biased and right-biased binary search variants used heresearch-in-rotated-sorted-arrayโ binary search on a non-trivially ordered array