Koko must finish all piles in h hours — find the minimum eating speed. The answer space [1, max(piles)] is sorted by feasibility, so a single binary search on the answer finds it in O(n log m) time.
Given piles of bananas piles and a guard arrival time h (in hours), Koko eats at most k bananas per hour from one pile per hour. Return the minimum integer k such that she can finish every pile within h hours.
Worked example: piles = [3, 6, 7, 11], h = 8. At speed k = 4: pile 3 → 1 h, pile 6 → 2 h, pile 7 → 2 h, pile 11 → 3 h — total 8 h ✓. At k = 3: pile 11 alone needs 4 h; total = 10 h > 8 ✗. Answer: 4.
k directly rather than on any array index.max(piles) (one hour per pile). The fastest valid minimum is 1. Search in [1, max(piles)].mid: sum(⌈pile / mid⌉ for pile in piles). This is O(n).hours <= h, mid is feasible — record it and try slower (hi = mid). Otherwise mid is too slow — try faster (lo = mid + 1).lo === hi the window has collapsed to exactly the minimum feasible speed.hi = mid or hi = mid - 1 when feasible. Because we want the minimum speed and mid itself might be the answer, set hi = mid (keep mid in the window). Use the lo < hi loop form — it terminates with lo === hi without an off-by-one.Space is O(1) — only a handful of pointers. The feasibility check itself is pure arithmetic; no sorting or extra allocation needed.
[3, 6, 7, 11], h = 8. Binary search speed k in [1, 11].1function minEatingSpeed(piles: number[], h: number): number {2▶ let lo = 1;3▶ let hi = Math.max(...piles); // max pile is a safe upper bound45 while (lo < hi) { // invariant: answer is in [lo, hi]6 const mid = (lo + hi) >> 1;7 const hours = piles.reduce((s, p) => s + Math.ceil(p / mid), 0);89 if (hours <= h) {10 hi = mid; // mid is feasible — try slower (smaller k)11 } else {12 lo = mid + 1; // mid is too slow — need faster k13 }14 }1516 return lo; // lo === hi: the minimum feasible speed17}
function minEatingSpeed(piles: number[], h: number): number {
let lo = 1;
let hi = Math.max(...piles); // max pile is a safe upper bound
while (lo < hi) { // invariant: answer is in [lo, hi]
const mid = (lo + hi) >> 1;
const hours = piles.reduce((s, p) => s + Math.ceil(p / mid), 0);
if (hours <= h) {
hi = mid; // mid is feasible — try slower (smaller k)
} else {
lo = mid + 1; // mid is too slow — need faster k
}
}
return lo; // lo === hi: the minimum feasible speed
}Math.max(...piles) is the safe upper bound because at that speed every pile takes exactly 1 hour. Any answer must live in this range.lo < hikeeps the loop running until the window collapses to a single value. The invariant is "the true minimum feasible speed is always in [lo, hi]".mid = (lo + hi) >> 1 avoids overflow. The feasibility check sums ⌈pile / mid⌉ for every pile — how many hours pile p takes at speed mid. Total time is O(n) per probe.hours <= h), hi = mid keeps mid in the window because it might be the minimum. If infeasible, lo = mid + 1 discards midentirely — it's confirmed too slow.lo === hi. That single remaining value is the minimum eating speed.lo to 1 (the problem guarantees piles[i] ≥ 1, but confirm constraints).k, iterate piles and assign each to the next ⌈pile/k⌉ consecutive hours.piles.length hours. Return -1 or throw as appropriate.h instead of k, or just use k = max(piles) and sum the ceiling hours.function minEatingSpeed(piles: number[], h: number): number {
let lo = 1;
let hi = Math.max(...piles); // max pile is a safe upper bound
while (lo < hi) { // invariant: answer is in [lo, hi]
const mid = (lo + hi) >> 1;
const hours = piles.reduce((s, p) => s + Math.ceil(p / mid), 0);
if (hours <= h) {
hi = mid; // mid is feasible — try slower (smaller k)
} else {
lo = mid + 1; // mid is too slow — need faster k
}
}
return lo; // lo === hi: the minimum feasible speed
}Speeds are monotone — faster always finishes no later. So just test k = 1, 2, 3, … and return the first speed whose total hours fit in h. Here m is the largest pile.
function minEatingSpeed(piles: number[], h: number): number {
const max = Math.max(...piles);
for (let k = 1; k <= max; k++) {
let hours = 0;
for (const p of piles) hours += Math.ceil(p / k);
if (hours <= h) return k; // first feasible speed is the minimum
}
return max; // worst case: eat the biggest pile per hour
}m candidate speeds, each an O(n) feasibility check, is O(m·n) and m can be 10⁹. Because feasibility is monotone in k, binary-searching the speed range turns the O(m) scan into O(log m).| "minimum speed / capacity / rate to finish in D days" | binary search on the answer |
| feasible(x) is monotone (once true, stays true) | binary search the threshold |
| ⌈a / b⌉ ceiling division in integer math | Math.ceil(a / b) or (a + b - 1) / b | 0 |
| want minimum-of-maximums or maximum-of-minimums | binary search + feasibility check |
function minEatingSpeed(piles: number[], h: number): number {
let lo = 1;
let hi = Math.max(...piles);
while (lo < hi) {
const mid = (lo + hi) >> 1;
const hours = piles.reduce((s, p) => s + Math.ceil(p / mid), 0);
if (hours <= h) hi = mid;
else lo = mid + 1;
}
return lo;
}piles = [3, 6, 7, 11], h = 8, the answer is:hours <= h), why do we set hi = mid rather than hi = mid - 1?lo < hi (not lo <= hi). What value does the function return?p at speed k in how many hours?