This problem is a classic example of the “Binary Search on Answer” pattern.
When we see a problem asking to find a “minimum” or “maximum” value (in this case, minimum eating speed k) that satisfies a certain condition (eating all bananas within h hours), and the solution space is monotonic, binary search is the way to go.
Why monotonic? Because if Koko can eat all bananas at speed k, she can definitely eat them at any speed > k. If she cannot eat them at speed k, she definitely cannot eat them at any speed < k.
The solution space for the eating speed k is between 1 (minimum possible speed) and max(piles) (at this speed, she eats any pile in exactly 1 hour).
We define a helper function canEatAll(speed) that computes the total hours required to eat all bananas at a given speed. If total_hours <= h, it’s a valid speed, and we try to find a smaller one (search left). Otherwise, we need a larger speed (search right).
// Pseudocode:
// max_speed = max(piles)
// For speed from 1 to max_speed:
// total_hours = 0
// For pile in piles:
// total_hours += (pile + speed - 1) / speed
// If total_hours <= h:
// return speed
#include <vector>
#include <algorithm>
class Solution {
public:
int minEatingSpeed(std::vector<int>& piles, int h) {
int max_speed = *std::max_element(piles.begin(), piles.end());
for (int speed = 1; speed <= max_speed; ++speed) {
long long hours = 0;
for (int pile : piles) {
hours += (pile + speed - 1) / speed; // Equivalent to ceil(pile / speed)
}
if (hours <= h) {
return speed;
}
}
return -1;
}
};// Pseudocode:
// low = 1, high = max(piles)
// result = high
// While low <= high:
// mid = low + (high - low) / 2
// total_hours = 0
// For pile in piles:
// total_hours += (pile + mid - 1) / mid
// If total_hours <= h:
// result = mid // This is a valid speed, record it
// high = mid - 1 // Try to find a smaller valid speed
// Else:
// low = mid + 1 // Speed too slow, increase it
// Return result
#include <vector>
#include <algorithm>
class Solution {
public:
int minEatingSpeed(std::vector<int>& piles, int h) {
int left = 1;
int right = *std::max_element(piles.begin(), piles.end());
int res = right;
while (left <= right) {
int mid = left + (right - left) / 2;
// Calculate total hours needed at speed 'mid'
long long hours = 0;
for (int pile : piles) {
// Integer math ceiling equivalent
hours += (pile + mid - 1) / mid;
}
if (hours <= h) {
res = mid;
right = mid - 1; // See if we can do better (smaller speed)
} else {
left = mid + 1; // Need to eat faster
}
}
return res;
}
};- Time Complexity:
O(N * log(M)), whereNis the number of piles andMis the maximum number of bananas in a pile. The binary search takesO(log(M))steps. In each step, we iterate through allNpiles to calculate the hours needed. - Space Complexity:
O(1). We only use a few integer variables (left,right,mid,hours,res), so the extra space required is constant.
hequals the number of piles (h == len(piles)): Koko must eat exactly one pile per hour. The optimal speed is exactlymax(piles). The binary search naturally converges to this.- Very large pile sizes and
h: This could potentially cause integer overflow when summing up hours, so we use along longforhoursin C++ to prevent overflow issues sincehcan be up to . - Small
h:his guaranteed to be>= len(piles). If it wasn’t, there would be no solution, but constraints prevent this.
- Recognition: Any problem phrasing that boils down to “Find the minimum capacity/speed/weight to achieve X within Y limit” screams Binary Search on Answer (e.g., Capacity To Ship Packages Within D Days, Split Array Largest Sum).
- Trick for ceiling division: In C++,
ceil(a / (double)b)can be rewritten using integer arithmetic as(a + b - 1) / b. This avoids floating-point inaccuracies and is generally faster. - Mentally visualize the timeline: the x-axis is speed
k, the y-axis is hoursh. It’s a monotonically decreasing curve. We just need to find the firstxwherey <= h.