Skip to content

贪心策略与正确性证明

基于通用算法套路 · 核于 2026-07

速查

  • 贪心策略 = 排序键 + 选择规则:几乎所有贪心题都是「按某个键排序 + 一个 for 循环选或不选」。排序键是策略的灵魂——键选错,整个策略崩。
  • 排序键怎么设计:问自己「什么样的元素应该优先被选 / 优先被排除?」。活动选择优先选结束早的(给后面留更多时间);Huffman 优先合并频率小的(避免高频字符长编码);分发糖果要双向扫描处理「相邻约束」。
  • 常见排序键陷阱:活动选择按开始时间排序(错,开始早但拖很久的活动会挤掉多个短活动);按时长最短排序(也错,反例可构造);按结束时间排序才对。
  • 正确性证明三方法:①交换论证(Exchange argument)——最常用,假设最优解与贪心解不同,把最优解逐步交换成贪心解且答案不变差,从而贪心解也是最优;②数学归纳法——对步数归纳「前 k 步贪心与某最优解一致」;③反证法——假设贪心解不是最优,导出矛盾。
  • 交换论证模板(背下来):设 OPT 是某最优解,GREEDY 是贪心解;①找到 OPT 与 GREEDY 第一个不同的选择;②用 GREEDY 的选择替换 OPT 中对应的选择,证明替换后仍是合法解且不劣化(目标函数不降);③反复替换直至 OPT 变成 GREEDY,故 GREEDY 也是最优。
  • 贪心失效判断 = 举反例:想否定一个贪心策略,构造一个小规模反例即可(如非标准币制找零、0-1 背包按单位价值)。能举出反例 ⇒ 贪心策略错 ⇒ 改排序键或上 DP。
  • 贪心 vs DP 的决策树:①先想贪心策略 + 试小例;②尝试交换论证证明;③证出来了 → 用贪心(O(n log n));④证不出来 / 举出反例 → 用 DP(保底最优)。
  • 「看起来对」≠「证明对」:贪心最危险的陷阱是策略直觉上合理就贸然用。没有证明的贪心等于在赌——面试/竞赛里没证明就写贪心,极可能挂。
  • 交互演示快速排序可视化 —— 贪心常依赖排序预处理。

一、贪心策略设计:排序键是灵魂

贪心策略的核心是回答两个问题:「按什么顺序处理元素」(排序键)和**「每个元素选还是不选」**(选择规则)。其中排序键决定了整个策略的成败。

设计排序键的方法论

问自己一个问题:「为了达成目标,什么样的元素应该优先处理?」

问题排序键直觉
活动选择(选最多不重叠)结束时间升序结束早的给后面留更多空间
区间调度(最多不相交区间)结束时间升序同上
分发糖果(相邻约束)不做单一排序,双向扫描先满足「比右邻分高则糖多」再满足左邻
Huffman 编码(最短带权路径)频率升序,优先队列低频字符放深处,编码长
任务调度(最大利润)利润降序 + 占槽位高利润任务先占靠后的截止槽
跳跃游戏(最少跳跃)不排序,扫最远可达每步贪心扩展可达边界

排序键选错的典型反例

以活动选择为例,对比三种排序键:

  • 按结束时间排序(正确)[1,3]、[2,5]、[4,6]、[6,7] → 选 [1,3]、[4,6]、[6,7] 共 3 个。
  • 按开始时间排序(错误):若有一个 [1,100] 的长活动排在最前,贪心先选它,会挤掉 [2,5]、[4,6]、[6,7] 等多个短活动,结果只剩 1 个。
  • 按时长最短排序(错误):可构造反例 [1,5]、[4,6]、[5,9]——最短是 [4,6](时长 2),先选它,但 [1,5] 与之冲突被排除,剩下只能选 [5,9],得 2 个;而按结束时间可得 [1,5]、[5,9] 也是 2 个——看似一样,换组数据 [1,4]、[3,5]、[4,6] 按时长选 [1,4][4,6] 都得 2 个,但能构造出按时长得 1、按结束得 2 的例子,故不可靠。

结论:排序键的正确性必须证明,不能靠「试几个例子感觉对」——下文用交换论证证明「按结束时间排序」的正确性。

二、正确性证明:交换论证

交换论证是贪心证明的最常用工具。核心思想:假设存在最优解 OPT 与贪心解 GREEDY 不同,把 OPT 逐步替换成 GREEDY 且保证每步替换后解不劣化,从而 GREEDY 也是最优

交换论证五步模板

  1. 设 OPT 为某个最优解,GREEDY 为贪心解。
  2. 找到 OPT 与 GREEDY 第一个不同的选择
  3. 用 GREEDY 的选择替换 OPT 中对应的选择,证明替换后仍合法(不违反约束)。
  4. 证明替换后目标函数不变差(仍最优)。
  5. 反复替换,OPT 最终变成 GREEDY,故 GREEDY 也是最优解。

实例:证明活动选择「按结束时间排序」正确

问题:n 个活动各有 (start, end),选最多互不重叠(前一个 end ≤ 后一个 start)的活动。

贪心策略:按 end 升序排序,第一个(结束最早)必选,之后每个 start ≥ 上一个 end 就选。

证明(交换论证)

  • 设最优解 OPT 选了 k 个活动 o₁, o₂, ..., oₖ(按结束时间升序),贪心解 GREEDY 选了 g₁, g₂, ..., gₘ(按结束时间升序,由排序保证 g₁.end ≤ o₁.end,因为 g₁ 是全局结束最早的)。
  • 比较第一个选择g₁.end ≤ o₁.end(贪心选的是结束最早的活动)。
  • 替换:把 OPT 中的 o₁ 换成 g₁。因为 g₁.end ≤ o₁.end ≤ o₂.start,所以 g₁o₂ 不冲突,替换后 OPT 仍选 k 个合法活动。
  • 归纳:对剩下的子问题(从 g₁.end 开始的活动)重复上述论证,每次贪心选择都能替换进 OPT 且不减少数量。
  • 结论:GREEDY 选的活动数 m = k,即贪心解也是最优解。证毕。

其他证明方法简述

  • 数学归纳法:对步数 i 归纳「前 i 步贪心选择与某个最优解的前 i 步一致」。基础步(i=1)证明第一个贪心选择安全;归纳步假设前 i 步一致,证明第 i+1 步也安全。
  • 反证法:假设贪心解不是最优(比最优解少),用贪心选择性质导出矛盾(如「最优解的第一步也能换成贪心选择且不变差」与「最优解已是最多」矛盾)。

实践建议:交换论证最直接、最好写,是面试/竞赛的首选。掌握「替换后仍合法 + 不劣化」这两步,绝大多数贪心证明都能搞定。

三、贪心失效判断:举反例

要否定一个贪心策略,构造一个小规模反例即可——不需要形式化证明。

找反例的思路

  1. 极端化:构造一个「贪心选择看似好但堵死更优方案」的输入。如长活动挤掉多个短活动、大面值硬币让总额凑不优。
  2. 优先怀疑「贪心选择会牺牲未来」:如果贪心选择「占用了某个稀缺资源」(时间槽、容量、位置),而更优方案需要保留这个资源——容易出反例。
  3. 对照已知贪心失效问题:找零(非标准币制)、0-1 背包(单位价值贪心)是经典失效例,新问题若有类似结构(选择影响后续)很可能也失效。

反例速查

问题失效的贪心策略反例
找零(币制 1/3/4,凑 6)每次选最大面值贪心得 4+1+1=3 枚,最优 3+3=2 枚
0-1 背包(容量 50)按单位价值降序全装贪心 160,最优 B+C=220
活动选择按开始时间 / 时长排序长活动挤掉多个短活动

决策规则:能举出反例 ⇒ 贪心策略错 ⇒ 换排序键重试,或直接上 DP。举不出反例且能交换论证 ⇒ 用贪心。

四、贪心 vs DP:怎么选

遇到最优化问题
  ├─ ① 先想贪心策略(排序 + for),试 2~3 个小例
  ├─ ② 尝试交换论证证明正确性
  │     ├─ 证出来 → 用贪心(O(n log n),快)
  │     └─ 证不出来 → 继续 ③
  └─ ③ 举反例 / 判断是否有「重叠子问题 + 最优子结构」
        ├─ 有反例 → 用 DP(保底最优)
        └─ 无反例但证明困难 → 仍倾向 DP 求稳

经验法则

  • 区间/调度类问题(活动选择、会议室、跳跃游戏)多优先试贪心。
  • 选择影响后续容量/状态(背包、零钱、编辑)多需要 DP。
  • 工程实践:贪心作为「第一尝试」,错了(反例 / WA)再上 DP——贪心写起来快,试错成本低。

五、常见贪心证明结论速查

问题贪心策略证明方法是否成立
活动选择 / 区间调度按结束时间排序交换论证✅ 成立
Huffman 编码优先队列合并最小频率交换论证 + 归纳✅ 成立
标准币制找零(1/5/10/25)每次最大面值数学归纳✅ 成立
非标准币制找零(1/3/4)每次最大面值反例 6=4+1+1❌ 失效 → DP
0-1 背包单位价值贪心反例(容量 50)❌ 失效 → DP
分数背包单位价值贪心交换论证✅ 成立
Dijkstra 最短路未确定点中选最短交换论证✅ 成立

交互演示

下一步

掌握了策略设计与证明方法后,下一步进入贪心的经典模型——活动选择、跳跃游戏、分发糖果、Huffman 编码以及 Dijkstra / Prim / Kruskal 的贪心本质,见经典贪心问题