Topics › Binary Search › BS on Answers
Koko eating bananas
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
Koko has n piles of bananas; pile i holds piles[i] bananas. The guards return in h hours. Koko picks an eating speed k (bananas per hour) and sticks to it. Each hour she chooses one pile and eats k bananas from it; if the pile has fewer than k left she finishes the pile and eats nothing else during that hour. She never switches piles mid-hour. Return the minimum integer speed k that lets her eat every banana within h hours.
Constraints: 1 ≤ n ≤ 10^4, n ≤ h ≤ 10^9, 1 ≤ piles[i] ≤ 10^9.
Example 1
piles = [3, 6, 7, 11], h = 8
answer = 4 // hours: ceil(3/4) + ceil(6/4) + ceil(7/4) + ceil(11/4) = 1 + 2 + 2 + 3 = 8
Example 2
piles = [30, 11, 23, 4, 20], h = 5
answer = 30 // h == n, so every pile must be finished in exactly one hour; the biggest pile dictates k
The feasibility check
Whatever approach we use, we need to answer one question quickly: at speed k, how many hours does Koko need? A pile of p bananas takes ceil(p / k) hours because she eats k per hour and the last hour may be partial. Summing over all piles gives the total.
static long hoursNeeded(int[] piles, int k) {
long hours = 0;
for (int p : piles) hours += ((long) p + k - 1) / k;
return hours;
}
Two details: (p + k - 1) / k is the standard integer formula for ceil(p / k) when both are positive; and the sum is a long because with 10^4 piles of 10^9 bananas and k = 1 the total is 10^13, far beyond int. The cast (long) p also keeps p + k - 1 from overflowing when p and k are both near 10^9.
Approach 1: Brute force, try every speed
Intuition. The speed never needs to exceed the largest pile: at k = max(piles) every pile takes exactly one hour, which is the fewest hours possible (n ≤ h guarantees that this always fits). So the candidate speeds are 1, 2, ..., max. Faster eating never needs more hours, so the first speed (from the slow end) that fits in h hours is the minimum.
Algorithm.
- Compute
max = max(piles). - For
k = 1, 2, ..., max: ifhoursNeeded(piles, k) ≤ h, returnk. - Return
max(never reached whenn ≤ h).
static int bruteForce(int[] piles, int h) {
int max = 0;
for (int p : piles) max = Math.max(max, p);
for (int k = 1; k <= max; k++) {
if (hoursNeeded(piles, k) <= h) return k;
}
return max;
}
Complexity. Time O(n * max): up to 10^9 speeds, each costing an O(n) scan. Space O(1). Hopeless for the given limits, but it states the idea we are about to speed up: the answer is the first k for which a monotonic predicate becomes true.
Approach 2: Optimal, binary search on the speed
Intuition. Define fits(k) = hoursNeeded(piles, k) ≤ h. If Koko can finish at speed k, she can also finish at any faster speed, because every ceil(p / k) term can only shrink when k grows. So over k = 1, 2, ..., max the predicate looks like F F F ... F T T T ... T, and we want the first T. That is a textbook binary search over the answer space, not over the array:
- If
fits(mid)is true,midis a valid speed; remember it and look for something slower on the left (hi = mid - 1). - If it is false, every slower speed also fails; look right (
lo = mid + 1).
Algorithm.
- Compute
max = max(piles); setlo = 1,hi = max,ans = max. - While
lo ≤ hi:mid = lo + (hi - lo) / 2.- If
hoursNeeded(piles, mid) ≤ h:ans = mid,hi = mid - 1. - Else:
lo = mid + 1.
- Return
ans.
static int optimal(int[] piles, int h) {
int max = 0;
for (int p : piles) max = Math.max(max, p);
int lo = 1, hi = max, ans = max;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (hoursNeeded(piles, mid) <= h) { ans = mid; hi = mid - 1; }
else lo = mid + 1;
}
return ans;
}
Dry run on piles = [3, 6, 7, 11], h = 8:
| lo | hi | mid | hours at mid | fits? | ans | next range |
|---|---|---|---|---|---|---|
| 1 | 11 | 6 | 1 + 1 + 2 + 2 = 6 | yes | 6 | [1, 5] |
| 1 | 5 | 3 | 1 + 2 + 3 + 4 = 10 | no | 6 | [4, 5] |
| 4 | 5 | 4 | 1 + 2 + 2 + 3 = 8 | yes | 4 | [4, 3] stop |
Result 4.
Complexity. Time O(n log max): about log2(10^9) ≈ 30 iterations, each an O(n) scan, so roughly 3 * 10^5 operations. Space O(1).
Why is the initial ans = max safe? Because fits(max) is always true (n hours, and n ≤ h), so if the loop somehow never records a value the fallback is still a valid answer. In practice the first mid that fits overwrites it.
The general recipe
This problem is the template for the entire “binary search on answers” section, so it is worth stating the recipe explicitly:
- Identify the quantity you are asked to minimise or maximise (here, speed
k). - Find its possible range (
[1, max(piles)]). - Write a feasibility function that decides, for a fixed candidate, whether the constraints hold (
hoursNeeded(k) ≤ h). - Argue that feasibility is monotonic in the candidate (faster is never worse).
- Binary search for the boundary, remembering the best feasible candidate seen.
Whenever steps 3 and 4 are possible, the search costs O(log(range)) feasibility checks regardless of how large the range is.
Edge cases and pitfalls
h == n. Every pile needs exactly one hour, so the answer is the largest pile. The second example shows this; the binary search finds it because everymid < maxleaves at least one pile needing two hours.- Single pile.
[7]withh = 7gives1; withh = 1gives7. Both are handled with no special code. - Overflow in the hour sum. With three piles of
805,306,368andk = 1, the total is2,415,919,104, which does not fit in anint. The Java test uses exactly this input and expects3; anintaccumulator would wrap to a negative number, makek = 1look feasible, and return the wrong answer. - Ceil arithmetic.
p / krounds down in Java. Use(p + k - 1) / k(withlong) or(p - 1) / k + 1; do not use floating-pointMath.ceilon adoubledivision, which can misround for large values. - Wrong search direction. On a feasible
midwe move left (seeking a smaller speed) and on an infeasiblemidwe move right. Swapping these produces the maximum feasible speed, which is justmax. lo = 0. Speed0would makehoursNeededdivide by zero. The range must start at1.
Related problems
- Find the smallest divisor is the same problem with the words changed: sum of ceilings under a threshold, minimise the divisor.
- Minimum days to make M bouquets searches over days with a greedy feasibility scan.
- Capacity to Ship Packages Within D Days and Book Allocation Problem minimise a capacity with a greedy partition check.
- Find square root of a number is the simplest instance of searching the answer space.