图解题库 / 二分答案

875. 爱吃香蕉的珂珂

中等 含交互动画

学完这道你应该能: 说清它为什么用「二分答案」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

每堆香蕉 piles[i] 根,每小时最多吃一堆里 k 根。要在 h 小时内吃完,求最小速度 k。

piles = [3, 6, 7, 11]
h = 8
输出 = 4

思路解析

最直接:速度 = 1、2、3… 挨个试,第一个「能在 h 小时内吃完」的就是答案。最坏要试到 max(piles) 那么大,香蕉堆里有上亿根就试上亿次。但有个规律——速度越大,耗时只会越短、不会忽长忽短,这种单调性正是二分的信号。

在速度区间 [1, max(piles)] 里二分:对每个候选速度 mid,算「吃完要几小时」。≤ h 说明 mid 够快,还能更慢,往左找;> h 说明太慢,往右找。

二分答案的关键是先写一个判定函数:输入速度 k,逐堆 ⌈piles[i]/k⌉ 求和,得到吃完要几小时。例如速度 6 → 1+1+2+2 = 6 小时。有了它,给任意速度都能一秒判断「够不够快」,再拿它和 h 比较来决定往左还是往右。

速度区间 [1, 11]:把候选速度排成一排(1~11),左指针 l=速度1,右指针 r=速度11。注意:二分的对象是速度,不是香蕉;判断靠上一步的判定函数。

mid=6 · 算耗时:试速度 6:每堆向上取整算小时,共 6 小时 ≤ 8,够快!记下它,再试更小的速度,r 左移。

mid=3 · 太慢:区间缩到 [1,6],试中间速度 3:要 10 小时 > 8,太慢了。speed 要更大,l 右移到 4。

mid=5 · 够快:区间 [4,6],试速度 5:正好 8 小时 ≤ 8,够快。继续找更小的,r 左移。

mid=4 · 够快:试速度 4:也是 8 小时 ≤ 8,够快。r 左移后区间只剩速度 4。

收敛:区间收敛到速度 4——这就是能 8 小时吃完的最小速度。

「求最小/最大的满足某条件的值」,且条件随值单调,就能二分答案:x 的平方根、送货能力都是它。

参考代码(Python)

Python
1def hours(k):                          # 判定函数:速度k要几小时
2    return sum(math.ceil(p/k) for p in piles)
3left, right = 1, max(piles)
4while left < right:
5    mid = (left + right) // 2
6    if hours(mid) <= h: right = mid    # 够快,试更小
7    else: left = mid + 1               # 太慢,加速
8return left

复杂度分析

  • 时间复杂度:O(n·log(maxPile)) —— 二分 log 次,每次判定 O(n)
  • 空间复杂度:O(1) —— 常数

套路模板

记住骨架:二分的是答案区间、核心是写对 check、合法时收右边求最小。求最大值就把收缩方向反过来。x 的平方根、D 天送货都是它。

Python
1# 求「满足条件的最小值」、且条件随值单调时套
2def check(x): ...                       # 判定:速度/容量 x 行不行
3left, right = 最小可能, 最大可能
4while left < right:
5    mid = (left + right) // 2
6    if check(mid): right = mid          # 行,收右边试更小
7    else: left = mid + 1               # 不行,加大
8return left

易错点

  • 在 piles 上二分 → ✅ 在「速度区间 [1, max]」上二分(二分的对象是答案(速度),不是数组)
  • 判定函数方向写反 → ✅ 耗时 ≤ h 时往「更小速度」找(我们要满足条件里的最小值)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →289. 生命游戏 ← 返回题库