leetcode
leetcode 3001 ~ 3050
爱吃香蕉的狒狒

爱吃香蕉的狒狒

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 43 ms, 内存: 17.1 MB


/*
 * 思路:
 * 1. 使用二分查找法找到吃香蕉的最小速度K。
 * 2. 初始化low为1(最小速度),high为piles中的最大值(最大速度)。
 * 3. 每次计算mid(low和high的中间值),并检查以该速度吃完所有香蕉是否在H小时内完成。
 * 4. 如果可以,则尝试更小的速度(high = mid);否则增加速度(low = mid + 1)。
 * 5. 最终,low就是所需的最小速度。
 */
import java.util.Arrays;
public class Solution {
    public int minEatingSpeed(int[] piles, int H) {
        int low = 1;
        int high = Arrays.stream(piles).max().getAsInt();
        while (low < high) {
            int mid = (low + high) / 2;
            if (canFinish(piles, H, mid)) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return low;
    }
    private boolean canFinish(int[] piles, int H, int K) {
        int hours = Arrays.stream(piles).map(pile -> (pile + K - 1) / K).sum();
        return hours <= H;
    }
}

解释

方法:

这个问题的核心是使用二分搜索来确定狒狒吃完所有香蕉的最小速度K。首先,定义一个辅助函数check(k),该函数用于验证以速度k是否可以在H小时内吃完所有香蕉。对于每堆香蕉,计算狒狒以速度k吃完它所需的小时数(向上取整),并累加所有堆所需的总小时数。如果总小时数小于等于H,返回真,否则返回假。然后,使用二分搜索在可能的速度范围内查找最小的K。速度的初始范围设置为从1到最大堆的香蕉数。二分搜索的逻辑是:如果中间速度mid可以在H小时内吃完所有香蕉,则尝试更小的速度区间;否则,尝试更大的速度区间。当左右边界相遇时,找到了最小的速度K。

时间复杂度:

O(N log M)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在二分搜索中使用`max(1, sum(piles) // h)`作为左边界?这个表达式的计算逻辑是什么?
在确定吃香蕉的最小速度时,我们希望确保速度的下限是合理的。如果将速度设为0或太低,则不可能在限定时间内吃完所有香蕉。表达式`sum(piles) // h`计算了平均每小时至少需要吃的香蕉数,以保证在`h`小时内能吃完所有香蕉。使用`max(1, sum(piles) // h)`是为了确保速度不低于1(因为速度不能为0),同时考虑到如果`sum(piles) // h`结果为0(当`sum(piles) < h`时),至少保证速度为1。这样的设置保证了速度的下限是实际情况下可能的最小速度,既实用又高效。
🦆
二分搜索中,为什么将右边界设置为`max(piles)`,而不是更大的值?
在确定狒狒吃香蕉的速度上限时,理论上狒狒每小时吃掉香蕉的最大速度不会超过最大堆中的香蕉数量,即`max(piles)`。这是因为即使速度设定得更高,狒狒也只能在一小时内吃掉一堆中的所有香蕉,而其余时间会空闲。因此,将右边界设置为`max(piles)`是合理的,它足以涵盖所有可能的情况而无需设置更高的值,这避免了不必要的计算,提高了算法效率。
🦆
在`check`函数中,为什么使用`ceil(num / k)`来计算吃完一堆香蕉所需的小时数,而不是直接使用`num / k`?
使用`ceil(num / k)`的原因是,这样可以正确处理每堆香蕉不是速度`k`的整数倍的情况。如果直接使用`num / k`,它会产生一个浮点数,可能会在不应该向下取整的场合向下取整,从而导致计算出的总时间少于实际需要的时间。例如,如果一堆有10根香蕉,速度是3根/小时,用`num / k`计算得3.33小时,但实际上需要4小时来吃完这堆香蕉(因为第四小时仍需吃香蕉)。因此,使用`ceil`确保任何有剩余的香蕉都会计入额外的一小时,这样可以保证时间计算的准确性。

相关问题