题目重点:把每堆耗时写成上取整,证明可行性对速度单调,再用边界不变量二分。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 每小时只处理一堆;某堆不足 k 时本小时吃完后不转去下一堆。piles 非空且均为正整数;h≥堆数,否则不存在可行速度。
- 速度 k 是正整数;使用 Python 任意精度整数。若输入可含空堆或题目允许一小时跨堆,公式需另改。
解题路线
- 固定速度 k,第 i 堆耗时 ceil(piles[i]/k),用 `(pile+k−1)//k` 做整数上取整。
- 若 k 可行,任何更大的速度也可行,因为每项上取整耗时非增;可行集合在整数轴上是一个后缀。
- 左界 1、右界 max(piles) 必定可行(h≥n)。令 mid 为中点;可行则保留 [low,mid],不可行则保留 [mid+1,high],直到重合。
- 求和中途超过 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) 是可行上界?
