【C/C++】875. 爱吃香蕉的珂珂

194 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情


题目链接:875. 爱吃香蕉的珂珂

题目描述

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

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

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

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

提示:

  • 1piles.length1041 \leqslant piles.length \leqslant 10^4
  • piles.lengthh109piles.length \leqslant h \leqslant 10^9
  • 1piles[i]1091 \leqslant piles[i] \leqslant 10^9

示例 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

整理题意

题目给定一组整数 piles,表示 n 堆香蕉(n 为数组 piles 的长度),以及给定时间 h,要求在给定时间内吃完所有香蕉,求可以在给定时间内吃完香蕉的最小速度。

题目规定 h 为小时制,且速度也为小时制,另外需要注意的是,对于一堆香蕉来说,需要花费整数小时吃完,也就是即使在吃完当前堆香蕉后还剩下不足一个小时的时间也需要等待一个小时之后才可以继续吃下一堆香蕉。

解题思路分析

首先观察题目数据范围,需要注意的是每一堆香蕉的个数最大是 10910^9,因为总共最多有 10410^4 堆香蕉,所以最大香蕉数量总和是超过了 int 的存储范围,需要使用更大的 long long 进行存储。

由于题目限制每堆香蕉最快也要 1 个小时才能吃完,所以最快吃香蕉的速度也只能是所有堆香蕉中最大值。

又因为吃香蕉的速度与吃完香蕉的用时成正比,也就是单调递增,如果当前速度能够在 h 小时内吃完,那么大于当前速度肯定也能在 h 小时内吃完,反之如果当前速度不能够在 h 小时内吃完,那么小于当前速度肯定也不能在 h 小时内吃完。由此我们可以使用 二分查找 来查找能够在 h 小时内吃完香蕉的最小速度。

具体实现

  1. 首先确定二分的对象以及区间,我们二分吃香蕉的速度,速度区间在 [1, max(piles)]
  2. 对于每次二分中的 check() 函数计算以当前速度吃完香蕉所需的时间,循环遍历数组中的每堆香蕉,计算用时,求累计用时。
  3. 根据当前速度所计算的总用时和 h 进行比较,如果当前时间大于 h,说明以当前速度不能在 h 时间内吃完香蕉,需要更快的速度。反之在小于当前速度中进行查找。
  4. 最后返回能够在 h 小时内吃完香蕉的最小速度。

复杂度分析

  • 时间复杂度:O(nlogm)O(n \log m),其中 n 是数组 piles 的长度,m 是数组 piles 中的最大值。需要 O(n)O(n) 的时间遍历数组找到最大值 m,二分查找需要执行 O(logm)O(\log m) 轮,每一轮二分查找需要 O(n)O(n) 的时间,因此总时间复杂度是 O(nlogm)O(n \log m)
  • 空间复杂度:O(1)O(1),仅需常数空间。

代码实现

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int h) {
        long long l = 0, r = 0;
        int n = piles.size();
        // 最大速度初始化为香蕉堆中的最大值,最快为 n 小时吃完
        for(int i = 0; i < n; i++) r = max(r, (long long)piles[i]);
        while(l + 1 != r){
            long long mid = (l + r) >> 1;
            //统计每堆香蕉需要多少时间
            long long temp = 0;
            for(int i = 0; i < n; i++){
                temp += piles[i] / mid;
                if(piles[i] % mid) temp++;
            }
            //能够在 h 小时内吃完,速度快了,降低速度求最小速度
            if(temp <= h) r = mid;
            //速度不够需要加速
            else l = mid;
        }
        return r;
    }
};

总结

  • 在题目中如果能够找到答案的 单调性,并且题目数据范围较大,无法暴力解决时,往往需要考虑 二分查找
  • 在二分查找中需要注意二分对象,二分区间,以及二分函数中的 check() 函数。
  • 在该题中计算总用时的时候需要注意数据可能很大,需要用 long long 进行存储,另外就是题目规定一堆香蕉必须以整数时间吃完,在吃完当前堆香蕉后剩余的不足一个小时的时间内不会再吃更多的香蕉。
  • 测试结果:

微信截图_20220613140348.png

结束语

生活,是一场充满未知的旅途。不要因为眼前的困境,就丧失对未来的期盼。以积极的心态,活出最佳状态,便是交给生活最好的一份答卷。愿你心怀希望,眼里有光亮,心中有期盼!