news 2026/9/17 12:26:26

Koko 吃香蕉问题:从暴力枚举到二分答案的单调性建模(LeetCode 875 解析)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Koko 吃香蕉问题:从暴力枚举到二分答案的单调性建模(LeetCode 875 解析)

Koko 吃香蕉问题:从暴力枚举到二分答案的单调性建模(LeetCode 875 解析)

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

本文以 LeetCode 875「Koko Eating Bananas(吃香蕉)」为对象,讲解如何从逐一试速的暴力解法过渡到基于答案空间二分(binary search on answer)的最优解。读完你将掌握:如何识别“可行域单调”这一可二分的前提、如何正确划定速度上下界、向上取整计时的正确写法,以及整数溢出等典型陷阱——这些技巧同样适用于“容量分配”“时间限制下求最小速率”一类问题。仓库中该题已有 Python、Java、C++、JavaScript、Go、Rust 等 12 种语言实现,均可作为对照参考。

问题定义

守卫离开h小时,香蕉堆为数组piles。Koko 每小时选择一堆吃,吃k根(不足k根则吃完该堆);某堆吃完后她才会换下一堆。求满足“h小时内吃完所有香蕉”的最小整数速度k

关键性质(后面推导的基础):速度越大,总耗时单调不减地变小。只要k = max(piles),每堆至多一小时即可吃完,总耗时至多为堆数,而题目保证存在合法解,因此答案必然落在[1, max(piles)]区间内。

前置知识

按 articles/eating-bananas.md 的 Prerequisites 一节,本题需要三个基础:

  • Binary Search(二分搜索):最优解不是对数据排序后找值,而是对“答案空间”(速度的取值区间)二分,这是本文档的核心思想;
  • Search Space Reduction(搜索空间缩减):识别单调性质,确认“速度→总耗时”是单调递减函数,且判定条件totalTime <= h在答案处由假变真,才可以用二分;
  • Ceiling Division(向上取整):每堆耗时必须向上取整——哪怕只剩 1 根,Koko 也会占用一整小时(吃满后停止,且题目规定每小时只能吃同一堆)。

解法一:暴力枚举所有速度

思路

speed = 1开始逐一尝试:对每个速度,累加每堆的ceil(pile / speed)得到总耗时,第一个满足totalTime <= h的速度即为答案。

算法步骤

  1. speed = 1
  2. 对每个速度,计算所有堆的ceil(pile / speed)之和;
  3. 若总和<= h,返回当前速度;
  4. 否则speed += 1重复。

代码示例

文档中给出了 Python、Java、C++、JavaScript、C#、Go、Kotlin、Swift、Rust 九种语言的暴力实现,此处以 Python 为例:

class Solution: def minEatingSpeed(self, piles: List[int], h: int) -> int: speed = 1 while True: totalTime = 0 for pile in piles: totalTime += math.ceil(pile / speed) if totalTime <= h: return speed speed += 1 return speed

C++ 版本展示了不依赖浮点的向上取整写法:

class Solution { public: int minEatingSpeed(vector<int>& piles, int h) { int speed = 1; while (true) { long long totalTime = 0; for (int pile : piles) { totalTime += (pile + speed - 1) / speed; // 整数上取整 } if (totalTime <= h) { return speed; } speed++; } } };

复杂度

  • 时间复杂度:$O(m \times n)$,其中 $n$ 是piles长度,$m$ 是单堆最大香蕉数(最坏要尝试到m才停);
  • 空间复杂度:$O(1)$。

h较小、答案接近max(piles)时,暴力法要线性扫过几乎整个区间,这就是二分优化的动机。

解法二:对答案空间二分

直觉与单调性论证

总耗时函数 $T(k) = \sum_i \lceil piles_i / k \rceil$ 关于 $k$ 单调不增。于是判定函数 $P(k): T(k) \le h$ 在速度轴上呈现“先假后真”的分界形态:

k: 1 2 3 4 5 6 ... max(piles) P(k): F F F T T T ... T ↑ 答案 = 最靠左的 T

这正是“找左边界”的标准二分模板:若mid可行,记录它并把right压到mid - 1继续向左找;否则把left抬到mid + 1

边界划定

  • left = 1:最小可能速度(不能为 0,否则除零);
  • right = max(piles):速度超过最大堆毫无收益——更大的堆本来就一小时吃完,更小的堆耗时只会更少。

文档 Common Pitfalls 一节特别指出,把上界设成sum(piles)是常见错误:区间虽然仍然正确,但比必要的更大,白白增加二分迭代次数。

算法步骤

  1. 设搜索区间left = 1right = max(piles),并初始化res = right
  2. left <= right:取mid = (left + right) // 2作为待测速度,计算总耗时;
  3. 若总耗时<= h:该速度可行,记录res = mid,搜左半区right = mid - 1
  4. 否则:速度太慢,搜右半区left = mid + 1
  5. 循环结束后返回res,即最小可行速度。

Python 实现

class Solution: def minEatingSpeed(self, piles: List[int], h: int) -> int: l, r = 1, max(piles) res = r while l <= r: k = (l + r) // 2 totalTime = 0 for p in piles: totalTime += math.ceil(float(p) / k) if totalTime <= h: res = k r = k - 1 else: l = k + 1 return res

复杂度

  • 时间复杂度:$O(n \times \log m)$,外层二分 $\log m$ 轮,每轮扫一遍 $n$ 个堆;
  • 空间复杂度:$O(1)$。

对比暴力法的 $O(m \times n)$,当m很大时(题目约束下可达 $10^9$)优势显著。

仓库中的多语言实现对照

仓库为该题提供了 12 份语言实现,它们与文档的算法骨架一致,但在工程细节上各有取舍,值得逐一比对:

文件二分形态值得注意的细节
python/0875-koko-eating-bananas.pywhile l <= r找左边界与文档最优解完全一致,res = r初始化保证有返回值
java/0875-koko-eating-bananas.javawhile (left < right)res变量right = middle/left = middle + 1收缩,最终return right;先线性扫一遍求right = max(piles)
cpp/0875-koko-eating-bananas.cppwhile (low <= high)并维护result文件头注释写明O(n x log m) / O(1),并用long int hours防溢出
javascript/0875-koko-eating-bananas.js开区间左边界,mid = (left + right) >> 1抽出getHourSpent纯函数计算耗时,便于单测
go/0875-koko-eating-bananas.go上界取10^9(题目约束上限)canEat在累加过程中提前返回(pruning),避免无效累加
rust/0875-koko-eating-bananas.rswhile l <= r((num_bananas - 1) / m) + 1实现无浮点向上取整
kotlin/0875-koko-eating-bananas.kt、typescript/0875-koko-eating-bananas.ts、swift/0875-koko-eating-bananas.swift、csharp/0875-koko-eating-bananas.cs、ruby/0875-koko-eating-bananas.rb、c/0875-koko-eating-bananas.c同一题目的其余语言版本,可作跨语言写法对照

从源码结构看,这些实现体现了三种常见的二分写法变体:①闭区间 +res记录(Python/C++/Rust/文档版);②半开区间left < right直接收敛(Java/JavaScript);③用固定大上界代替max(piles)(Go)。三种写法正确性等价,选择哪种取决于团队习惯——文档采用的是第一种,边界语义最直观。

常见陷阱(Common Pitfalls)

文档专设 Pitfalls 一节总结了三个高频错误,均有多语言代码佐证。

1. 用整除代替向上取整

Koko 吃不满一小时也不会“省时间”——只要还有剩余就占满整小时,因此必须向上取整:

# 错误:整除向下取整 totalTime += pile // speed # 正确:向上取整 totalTime += math.ceil(pile / speed) # 或不依赖 math 模块的整数写法: totalTime += (pile + speed - 1) // speed

C++ 版 cpp/0875-koko-eating-bananas.cpp 用浮点ceil实现,Rust 版 rust/0875-koko-eating-bananas.rs 用(x - 1) / m + 1整数实现,两者殊途同归。注意浮点ceil对极大整数(接近 $2^{53}$)存在精度风险,竞赛与生产代码更推荐纯整数上取整。

2. 二分边界设错

# 错误:从 0 开始会除零 l = 0 # 错误:上界无谓放大(正确但区间过大) r = sum(piles) # 正确边界 l, r = 1, max(piles)

下界为 0 会直接引发除零异常;上界用sum(piles)虽然逻辑正确,但会把二分轮数放大到 $\log(\text{sum})$。go/0875-koko-eating-bananas.go 则取了第三个极端——直接用题目约束 $10^9$ 作上界,正确性依赖题目给定的数据范围,脱离题目约束时不如max(piles)稳健。

3. 总耗时的整数溢出

对每堆耗时求和时,nm均很大时总和可能超过 32 位整型上限:

// 错误:可能溢出 int totalTime = 0; // 正确:使用 long long totalTime = 0;

仓库实现对此处理一致:Java 用long totalTime,C++ 用long long(暴力版)/long int(二分版),Rust 用i64累加。跨语言读者迁移代码时,这一点最容易漏掉。

小结

  • 本题的建模关键是发现“速度→耗时”的单调性,把优化问题转化为“有序判定序列上找第一个真值”的分界问题;
  • 最优解为 $O(n \times \log m)$,暴力为 $O(n \times m)$,二分把外层从线性降为对数;
  • 三个工程要点必须落实:向上取整计时、边界[1, max(piles)]、耗时累加用 64 位整数;
  • articles/eating-bananas.md 提供了九种语言的完整对照代码,仓库中 12 份语言实现(如 python/0875-koko-eating-bananas.py、java/0875-koko-eating-bananas.java)则展示了左边界二分的多种等价写法,可结合阅读。

掌握“二分答案”这一模式后,遇到“给定约束求最小化/最大化某个参数”的题型(如装运包裹、按天分配容量等),都可以按“界定答案区间 → 写单调判定函数 → 二分找分界”三步套用。

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 12:25:58

Macro数据库规则CS-01到CS-38深度解读:资深Rust工程师的DB哲学

Macro数据库规则CS-01到CS-38深度解读&#xff1a;资深Rust工程师的DB哲学 【免费下载链接】macro Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — -linked together with shared AI memory. 项目地址: https://gitcode.com/…

作者头像 李华
网站建设 2026/9/17 12:25:30

被Turnitin检测AI痕迹?三款降AIGC软件对比测评

如果你是一名留学生、硕博研究生&#xff0c;或是任何需要进行英文学术写作的创作者&#xff0c;过去一年你一定反复被一个问题困扰&#xff1a;"我明明是用AI辅助写作&#xff0c;为什么Turnitin等检测器总说我有AI痕迹&#xff1f;" 随着AIGC技术的快速发展&#x…

作者头像 李华
网站建设 2026/9/17 12:24:20

OWASP Juice Shop 快速入门及实战指南

OWASP Juice Shop 快速入门及实战指南 【免费下载链接】juice-shop OWASP Juice Shop: Probably the most modern and sophisticated insecure web application 项目地址: https://gitcode.com/gh_mirrors/ju/juice-shop 一、项目介绍 OWASP Juice Shop 是一个高级且充…

作者头像 李华
网站建设 2026/9/17 12:23:28

芯片验证-AXI详解

outstanding指的是axi可以发出最多多少个操作而不需要等带response&#xff1b;回卷是burst的类型的一种&#xff0c;是指从某个地址开始访问&#xff0c;增加到一定的地址后继续回到其实的地址进行访问&#xff1b;非对齐是指不是按照数据的宽度进行地址增加访问&#xff0c;例…

作者头像 李华
网站建设 2026/9/17 12:21:36

擦亮眼睛!不是随便一个 AI 就能搞定毕业论文,2026 导师认可工具全览

每年毕业季&#xff0c;无数同学深陷论文难题&#xff1a;开题毫无思路、搭建框架耗费数日、初稿逻辑松散、查重标红泛滥、AI检测超标、格式反复被导师驳回。现如今市面上通用型AI工具遍地开花&#xff0c;但绝大多数通用大模型存在编造虚假参考文献、学术语句口语化、AI生成痕…

作者头像 李华