贪心选择
复习
- 分治:把问题拆成更小的子问题
- 算法与正确性:算法需要论证
- 最短路径与最小生成树:其中用到贪心
TL;DR
- 贪心每一步只选当前看起来最好的
- 它简单快速,但不一定得到全局最优
- 有些问题贪心恰好正确,有些则会出错
- 用贪心之前,必须先确认它是否正确
正文
前面在求最短路、最小生成树时,我们其实已经用过一种思路:每一步都选当前最好的。 这种思路有个名字——贪心(greedy)。
只看眼前
贪心的做法是:每一步都选择当前状态下看起来最优的那个选项,选完就往前走,不再回头。
它简单、直接、通常很快,因为没有复杂的搜索或回溯。Dijkstra 每次确定最近的节点、Kruskal 每次加最小的边,都是贪心。
但它不一定对
麻烦在于:眼前最优,未必是全局最优。 贪心可能在某个局部做了“聪明”的选择,却把通向更好结果的路堵死了。
一个经典的例子是找零钱。如果硬币面额是 1、5、10、25,想凑出某个金额,贪心(每次尽量用大面额)往往是对的。可如果面额设计得“奇怪”一些,贪心就可能凑出更多枚硬币——正确答案需要少用大面额、多用其他组合。
这说明:贪心不是一种“万能方法”,而是一种需要“碰运气”的策略。 它能不能用,取决于问题本身。
关键问题
所以,面对一个问题,贪心能不能用,要回答一个尖锐的问题:
“当前最优”的选择,会不会影响后面做出全局最优?
如果不会——比如选了这个最优,仍然能达到全局最优——那贪心就成立。这类问题常有一个性质:最优解包含了对当前最优的选择。 这需要证明,而不是想当然。
有些问题贪心确实成立(最小生成树、活动选择),有些则不成立(0/1 背包)。所以设计贪心时,最重要的不是写出代码,而是判断它到底对不对。 下一章就来看看,怎样证明或推翻一个贪心策略。
思考题 1
贪心策略的核心思想是什么?
思考题 2
为什么贪心不一定能得到全局最优?试举一个会出错的例子。
小结
知识点
- 贪心每步选择当前最优,且不回头
- 它简单快速,但不保证全局最优
- 有些问题贪心正确,有些会出错
- 使用前必须论证其正确性
参考资料
- Wikipedia(zh):贪心算法:每步选当前最优的算法策略
- Wikipedia(zh):找零问题:贪心可能失效的经典例子
思考题答案(仅供参考)
思考题 1
每一步都只考虑当前状态,选择看起来最好的那个选项,做出选择后不再回头、不做回溯。它追求的是每一步的局部最优。
思考题 2
因为局部最优未必导向全局最优。贪心的选择可能封死了通向更优解的路。比如面额设计特殊时,找零钱的贪心会用到更多硬币,而全局最优需要放弃某个大面额、采用另一种组合。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪