Leetcode 剑指 Offer II 073.爱吃香蕉的狒狒

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

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

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

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

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

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

题目思考

  1. 如何优化时间复杂度?

解决方案

思路
  • 分析题目, 最容易想到的思路就是从速度 1 开始遍历, 基于当前速度计算吃完所有香蕉的时间, 如果小于 h 就直接返回
  • 不过这样时间复杂度达到了 O(NM), 其中 N 是香蕉堆数, M 是速度上限, 即所有堆的最大香蕉根数 (没必要使用更大的速度, 因为每次只能吃一堆)
  • 根据题目规模, 这种做法肯定会超时, 如何优化呢?
  • 不难发现, 当速度增大时, 吃完香蕉的时间一定是递减的, 这里存在单调性
  • 我们可以利用这一点, 采用二分查找的方式加速
  • 也就是基于速度上下限 1 和 M, 二分查找满足要求的最小速度: 如果当前速度不满足要求, 则向右半区间查找; 否则更新最终结果为较小值, 并向左半区间查找
  • 而判断某个速度是否满足要求也很简单: 直接遍历所有堆, 累加每一堆按照当前速度吃完所需的时间, 如果时间总和不超过 h, 则满足要求, 否则不满足要求
  • 最后, 遍历完整个区间后的最终结果即为满足要求的最小速度
  • 下面代码中有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O(NlogM): N 是香蕉的堆数, M 是所有堆的最大香蕉根数, 需要在区间[1,M]之间进行二分查找 (O(logM)), 而判断当前速度是否有效, 需要遍历整个堆 (O(N)), 所以整体时间复杂度就是 O(NlogM)
  • 空间复杂度 O(1): 只使用了几个常数空间的变量
代码
class Solution:
    def minEatingSpeed(self, piles: List[int], h: int) -> int:
        # 二分查找+有效性判断
        # 最小速度是1, 最大速度是所有堆的最大香蕉根数
        # 没必要使用更大的速度, 因为每次只能吃一堆
        s, e = 1, max(piles)
        res = e

        def isValid(speed):
            # 判断使用当前速度吃香蕉时, 能否在h小时内吃完
            hour = 0
            for p in piles:
                # 当前堆需要吃ceil(p/speed)小时
                hour += math.ceil(p / speed)
                if hour > h:
                    # 总时间超过了h, 无效
                    return False
            # 可以在h小时内吃完
            return True

        # 二分查找满足要求的最小速度
        while s <= e:
            m = (s + e) >> 1
            if isValid(m):
                # 当前速度有效, 更新res为两者较小值
                res = min(res, m)
                # 然后向左查找, 检查是否存在更小的有效速度
                e = m - 1
            else:
                # 当前速度无效, 只能向右查找
                s = m + 1
        return res

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

  • 20
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值