一、何时用贪心?关键条件
-
贪心选择性质:
当前局部最优选择一定能导致全局最优解,且不需要回溯或比较其他可能性。
例如:找零问题时,优先用大面额硬币一定能得到最少硬币数(在硬币面额是倍数关系时)。 -
最优子结构:
问题的全局最优解可以通过局部最优解递推得到,且子问题独立。
例如:活动选择问题中,每次选最早结束的活动,剩余子问题仍然是同类优化问题。 -
无后效性:
当前选择一旦做出,后续决策不受之前选择的影响。
例如:Dijkstra算法中,一旦确定某个节点的最短路径,不会被后续更新。
二、典型贪心算法问题举例
1. 区间调度问题(Activity Selection Problem)
-
问题:选择最多数量的互不重叠的区间。
-
贪心策略:每次选结束时间最早的区间,给后续留更多空间。
-
为何不用DP:DP需要记录所有子集组合(O(2ⁿ)),而贪心只需排序后遍历(O(n log n))。
2. 霍夫曼编码(Huffman Coding)
-
问题:用最小二进制编码压缩数据(频率高的字符用短编码)。
-
贪心策略:每次合并频率最低的两个节点,构建二叉树。
-
为何不用DP:合并顺序的局部最优直接导致全局最优,无需保存中间状态。
3. 找零问题(Coin Change, 特定面额)
-
问题:用最少数量的硬币凑出金额(如面额为1,5,10)。
-
贪心策略:每次选最大面额硬币。
-
为何不用DP:当硬币面额是倍数关系(如1,5,10)时贪心成立,否则需DP(如面额1,3,4,凑6时贪心失效)。
4. 最小生成树(Prim/Kruskal算法)
-
问题:连接所有节点的边权值和最小的树。
-
贪心策略:
-
Kruskal:每次选权值最小的边,且不形成环。
-
Prim:从任意节点开始,每次选连接已选集合的最小边。
-
-
为何不用DP:生成树的子问题无需记录路径,贪心的局部选择直接保证全局最优。
5. Dijkstra算法(无负权边的最短路径)
-
问题:单源最短路径(无负权边)。
-
贪心策略:每次从优先队列中选当前距离最短的节点,松弛其邻边。
-
为何不用DP(如Bellman-Ford):无负权边时,局部最短路径即全局最短,无需重复松弛。
三、贪心 vs DP的对比案例
案例1:背包问题
-
0-1背包问题:必须用DP,因为每个物品选/不选会影响后续决策。
-
分数背包问题:可以用贪心(按单位价值排序,优先拿高价值密度的物品)。
案例2:股票买卖问题
-
一次交易(LeetCode 121):贪心(记录历史最低点,计算当前最大利润)。
-
多次交易(LeetCode 122):贪心(所有上涨区间都买入)。
-
含冷却期(LeetCode 309):必须用DP,因为当前状态依赖前两天的决策。
四、如何判断是否用贪心?
-
尝试举反例:验证贪心策略是否能覆盖所有情况。
例如:非倍数面额的找零问题(如1,3,4),贪心可能失败(凑6:4+1+1 vs 3+3)。 -
检查问题结构:若问题需要“所有可能的组合”或“撤销之前选择”,则必须用DP。
-
数学证明:通过归纳法或交换论证证明贪心选择的正确性。
五、总结
-
用贪心的信号:
-
问题可以通过单向的局部最优选择推进。
-
无需保存历史状态或比较多种路径。
-
贪心策略能被严格证明正确。
-
-
用DP的信号:
-
问题需要穷举所有可能性或存在重叠子问题。
-
当前决策依赖之前的所有选择(如背包问题)。
-
经典口诀:
-
贪心:“短视但高效,一步定终身”。
-
DP:“记忆化搜索,全局揽乾坤”。
1783

被折叠的 条评论
为什么被折叠?



