珂珂喜欢吃香蕉。这里有 n 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 h 小时后回来。

珂珂可以决定她吃香蕉的速度 k (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 k 根。如果这堆香蕉少于 k 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。  

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 h 小时内吃掉所有香蕉的最小速度 kk 为整数)。

示例 1:

**输入:**piles = [3,6,7,11], h = 8 **输出:**4

示例 2:

**输入:**piles = [30,11,23,4,20], h = 5 **输出:**30

示例 3:

**输入:**piles = [30,11,23,4,20], h = 6 **输出:**23

提示:

  • 1 <= piles.length <= 10^4
  • piles.length <= h <= 10^9
  • 1 <= piles[i] <= 10^9

思路

这题正着想很困难的。但有一个很明显的点,速度最大为 piles 中最大的元素,记为 M. 这样,每次都能吃一堆,题目保证 piles.length <= h, 按这个速度吃,肯定是能吃完的。如果 h < piles.length, 无论啥速度都不行,因为题目说每次吃完一堆,这个小时内不再吃别的。

所以暴力的想法是枚举 1 到 M 的所有值,试试用该速度能不能吃完,然后返回最小速度。但我都想到这一步了,难道还不能加快一下搜索思路?

  • 每次对半猜答案,如果当前答案满足要求,往小了找;
  • 如果当前答案不满足要求,往大了找。
  • 如此往复,最后剩三个答案,取最小可行的就完了。

以前从来没遇到过,二分法还能这么用。果然,刷题还是能锻炼思维的。

Code

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int h) {
        int end = -1;
        for (int p : piles) {
            end = max(end, p);
        } // end must be feasible
 
        int beg = 1, mid = 1;
        while (beg <= end) {
            mid = (beg + end) / 2;
            if (feasible(piles, h, mid)) {
                end = mid - 1;
            } else {
                beg = mid + 1;
            }
        }
        return end + 1; // last feasible we do `end = mid - 1`, so here we revert this.
    }
 
    bool feasible(const vector<int>& piles, int h, int k) {
        for (int p : piles) {
            h -= ((p - 1) / k + 1);
            if (h < 0) return false;
        }
        return true;
    }
};