MLE · Coding Screen

Apple MLE Koko 吃香蕉:为什么能二分最小速度

把每堆耗时写成上取整,证明可行性对速度单调,再用边界不变量二分。

题目重点:把每堆耗时写成上取整,证明可行性对速度单调,再用边界不变量二分。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 每小时只处理一堆;某堆不足 k 时本小时吃完后不转去下一堆。piles 非空且均为正整数;h≥堆数,否则不存在可行速度。
  • 速度 k 是正整数;使用 Python 任意精度整数。若输入可含空堆或题目允许一小时跨堆,公式需另改。

解题路线

  1. 固定速度 k,第 i 堆耗时 ceil(piles[i]/k),用 `(pile+k−1)//k` 做整数上取整。
  2. 若 k 可行,任何更大的速度也可行,因为每项上取整耗时非增;可行集合在整数轴上是一个后缀。
  3. 左界 1、右界 max(piles) 必定可行(h≥n)。令 mid 为中点;可行则保留 [low,mid],不可行则保留 [mid+1,high],直到重合。
  4. 求和中途超过 h 可提前停止;7,770 组短输入以逐一试探所有速度的独立程序核对。

手动走一遍

piles=[3,6,7,11]、h=8:k=4 时耗时 1+2+2+3=8 可行;k=3 时 1+2+3+4=10 不可行,因此最小速度是 4。h 恰等于堆数时答案为最大堆大小。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

def minimum_eating_speed(piles: list[int], hours: int) -> int:
    """Minimum positive integer k with sum(ceil(pile/k)) <= hours."""
    if not piles or any(pile <= 0 for pile in piles):
        raise ValueError("piles must be non-empty and positive")
    if hours < len(piles):
        raise ValueError("one pile needs at least one hour")

    low, high = 1, max(piles)
    while low < high:
        mid = (low + high) // 2
        used = 0
        for pile in piles:
            used += (pile + mid - 1) // mid
            if used > hours:
                break
        if used <= hours:
            high = mid
        else:
            low = mid + 1
    return low

复杂度

每次判定 O(n),二分 O(log M) 次,合计 O(n log M) 时间、O(1) 额外空间,M=max(piles)。高位语言也应避免把整数上取整改为浮点除法。

易错点与追问

容易失分的边界

  • 把一小时内剩余容量接着吃下一堆,错误地用 ceil(总量/k)。
  • h<n 时依旧返回最大堆而不说明无解。
  • 二分更新可行端时用 mid−1,跳过最小可行速度。
  • 大输入逐项相加溢出;固定宽度语言应使用足够宽的整数或超过 h 即停。

继续深挖

  • 若允许每小时跨堆,耗时公式变成什么?
  • 若不同堆有切换时间,单调判定还能保持吗?
  • 如果速度有成本函数,如何在可行速度中选择?
  • 为什么 max(piles) 是可行上界?
✓已复制微信号!