Find the slowest eating speed that finishes all piles within the time limit.
Start with the concrete trace below. It shows the state the algorithm must carry as it runs.
Problem trace
Koko Eating Bananas: Binary-search the first speed whose ceiling-division total is at most 8 hours.
Input and goalFind the slowest eating speed that finishes all piles within the time limit.
Test the initial midpointInitialize left=1 and right=max(piles)=11, then test speed floor((1+11)/2)=6. Hours are 1+1+2+2=6, so 6 <= 8 and right becomes 6.
left1021324354speed65768798109right1110
inputpiles [3,6,7,11], hours 8testedk=6: ceil(3/6)+ceil(6/6)+ceil(7/6)+ceil(11/6)=1+1+2+2=6decision6 <= 8: right = 6
Speed 3 is too slowAt speed 3, hours are 1+2+3+4=10. Since 10 > 8, speeds 1..3 are infeasible; set left=3+1=4.
left1021speed324354right657687981091110
inputpiles [3,6,7,11], hours 8testedk=3: ceil(3/3)+ceil(6/3)+ceil(7/3)+ceil(11/3)=1+2+3+4=10decision10 > 8: left = 4
Speed 5 is feasibleAt speed 5, hours are 1+2+2+3=8. Since 8 <= 8, speed 5 may be the first feasible speed, so set right=5 and test speed 4.
102132left43speed54right657687981091110
inputpiles [3,6,7,11], hours 8testedk=5: ceil(3/5)+ceil(6/5)+ceil(7/5)+ceil(11/5)=1+2+2+3=8decision8 <= 8: right = 5
Speed 4 is feasibleAt speed 4, hours are 1+2+2+3=8. Keep this feasible candidate by setting right=4; now left and right meet.
102132leftspeed43right54657687981091110
inputpiles [3,6,7,11], hours 8testedk=4: ceil(3/4)+ceil(6/4)+ceil(7/4)+ceil(11/4)=1+2+2+3=8decision8 <= 8: right = 4
Return the first feasible speedThe interval has converged at speed 4. Speed 3 needed 10 hours, so 4 is not merely feasible; it is the minimum feasible speed.
The answer is numeric, bounded from 1 to max(piles), and feasibility changes only once: if speed k finishes in time, every faster speed also finishes.
Keep true
The minimum feasible speed always remains in inclusive [left, right]. An infeasible midpoint and every slower speed are removed; a feasible midpoint is retained as right.
Reuse it
For capacity, rate, and threshold problems, define a monotone yes/no test, choose bounds that contain the answer, and retain the midpoint on the side that may hold the first true value.
Read it this way: Initialize left=1 and right=max(piles)=11, then test speed floor((1+11)/2)=6. Hours are 1+1+2+2=6, so 6 <= 8 and right becomes 6. Step through the frames to watch the state change. The last frame shows the answer or the stopping condition.
Pattern: Binary search on the answer.
Simple idea: Try a speed. If it is fast enough, a faster speed is also fast enough. This
creates one false-to-true boundary. Search for the first true speed.
def min_eating_speed(piles: list[int], hours: int) -> int: left, right = 1, max(piles) while left < right: speed = (left + right) // 2 time = sum((pile + speed - 1) // speed for pile in piles) if time <= hours: right = speed else: left = speed + 1 return left
Cost:O(nlogm) time and O(1) space, where m is the largest pile.