题意
有一只狒狒,要在 H H H 个小时里吃完 p i l e s piles piles 数组中的香蕉,第 i i i 堆香蕉中有 p i l e s i piles_i pilesi 根香蕉。狒狒可以决定她吃香蕉的速度,为 K K K。每个小时,她将会按顺序选择一堆香蕉,从中吃掉 K K K 根。如果这堆香蕉少于 K K K 根,她将吃掉这堆的所有香蕉,下一个小时开始吃另一堆香蕉。
现在给出 p i l e s piles piles 数组和时间 H H H,求出狒狒在能吃完所有香蕉的情况下,最小的速度 K K K 是多少。
分析
当我们读题的时候,看到:
能吃完所有香蕉的情况下(也就是吃最多的香蕉),最小的速度 K K K。
浓缩一下,就是:
求最大值(吃的香蕉数)中的最小值(速度 K K K)。
这句话是二分的代表。那么我们就能想到使用二分答案来实现程序。
在使用二分答案进行写代码时,我们要考虑以下几点:
一、确定二分答案的边界:左边界 l l l 为 1 1 1,代表速度 K K K 最小为 0 0 0(因为在没有香蕉的情况下,速度为 0 0 0);右边界 r r r 为 max ( [ p i l e s 1 , p i l e s n ] ) \max([piles_1,piles_n]) max([piles1,pilesn]),即香蕉最多的那一堆的香蕉数(因为就算是时间再不够,吃最多的香蕉也能达到要求)。
二、检查二分出来的答案:对于每个答案 k e y key key,使用 a n s ans ans 算出对于当前速度 k e y key key,吃完这些香蕉的总时间,检查是否小于等于给出的时间 H H H,如果是,答案可行,寻找更小答案;否则寻找更大的、合法的答案。
三、二分的左右边界更换:对于 check ( m i d ) \text{check}(mid) check(mid),如果成立,那么寻找更小答案,即右边界 r r r 为 m i d mid mid;如果不成立,那么寻更大合法答案,即左边界 l l l 为 m i d − 1 mid-1 mid−1。
Code
// 073.爱吃香蕉的狒狒
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
int x, h;
vector <int> vec;
class Solution {
public:
bool check(int key, int n, int h, vector <int>& nums) {
int ans = 0;
for (int i = 0; i < n; i++) {
if (nums[i] % key != 0) ans = ans + nums[i] / key + 1;
else ans = ans + nums[i] / key;
}
return ans <= h;
}
int minEatingSpeed(vector <int>& piles, int h) {
int l, r, max1 = 0, n = piles.size();
for (int i = 0; i < n; i++) max1 = piles[i] > max1 ? piles[i] : max1;
l = 0, r = max1;
while (l < r) {
int mid = (l + r) >> 1;
if (check (mid, n, h, piles)) r = mid;
else l = mid + 1;
}
return l;
}
};
int main() {
Solution Type;
while (scanf ("%d", &x) == 1) vec.push_back(x);
h = vec.back();
vec.pop_back();
printf ("%d", Type.minEatingSpeed(vec, h));
return 0;
}
完结撒花。