贪心法(Greedy Algorithm)

1. 核心思想

贪心法在每一步决策时都选择当前状态下最优的选项,并且一旦做出选择就不再回溯。它期望通过一系列局部最优选择得到全局最优解——这个期望并不总是成立,因此贪心法必须配证明。

2. 两个基本要素

2.1 贪心选择性质(Greedy Choice Property)

所求问题的整体最优解可以通过一系列局部最优的选择达到。换句话说,做第一次贪心选择时,不必考虑子问题的解,就可以断定它一定属于某个最优解。

证明方法:交换论证(exchange argument)。设原问题有一个最优解 $O$,若 $O$ 的第一步选择与贪心选择 $g$ 不同,则构造解 $O'$ 把 $O$ 中的该选择替换为 $g$,证明 $O'$ 仍是最优解(不劣于 $O$),从而说明存在包含贪心选择的最优解。

2.2 最优子结构

与动态规划相同:原问题的最优解包含子问题的最优解。

3. 贪心法 vs 动态规划

对比项贪心法动态规划
决策方式每步只做一个选择,不回溯枚举所有划分/选择,取最优
依赖性质贪心选择性质 + 最优子结构最优子结构 + 重叠子问题
计算方向通常自顶向下依次决策自底向上填表
复杂度一般更低(常为 $O(n\log n)$)一般更高(常为 $O(n^2)$、$O(n^3)$)
风险贪心选择性质不成立时结果错误状态设计正确则一定正确

经典反例:0-1 背包问题不能用「单位价值最高优先」的贪心法。例如容量 10,物品为 (重量 6, 价值 12)、(重量 5, 价值 9)、(重量 5, 价值 9),单位价值最高的是第一件,贪心选它后剩余容量 4 装不下任何物品,总价值 12;而最优解是选后两件,总价值 18。背包问题(物品可分割)则可以用贪心法。

4. 典型问题

问题贪心策略复杂度
活动安排按结束时间升序,能兼容就选$O(n\log n)$
哈夫曼编码每次合并权值最小的两棵树$O(n\log n)$
最小生成树(Kruskal)按边权升序,不构成环就加入$O(e\log e)$
最小生成树(Prim)每次选连接已选集合的最小边$O(n^2)$
单源最短路径(Dijkstra)每次选未确定集合中距离最小的点$O(n^2)$ / $O(e\log n)$
多机调度最长处理时间作业优先$O(n\log n)$
背包问题(可分割)单位价值降序装填$O(n\log n)$
找零钱(面值规范时)优先用大面值$O(n)$

5. 常见易错点

  • Dijkstra 不能处理负权边:负权会破坏「已确定点的最短距离不会再变小」这一贪心前提,此时应改用 Bellman-Ford。
  • 哈夫曼编码不是唯一的:存在相同权值时合并顺序可以不同,但最优编码的平均码长(WPL)唯一。
  • 贪心解不唯一但最优值唯一:活动安排、最小生成树都可能存在多个不同的最优方案,代价相同。