动态规划 vs 贪心算法

概述

动态规划(Dynamic Programming)和贪心算法(Greedy Algorithm)都是求解优化问题的经典方法,两者都利用最优子结构性质,但在求解顺序适用条件上有本质区别。动态规划自底向上地考虑所有子问题,而贪心算法在每一步做出局部最优选择。

对比表格

维度动态规划贪心算法
求解顺序先求解子问题,再基于子问题的解做出选择先做出选择,再求解剩下的子问题
子问题依赖子问题之间可能相互依赖,需要求解所有相关子问题子问题之间相互独立,做出选择后只需求解一个子问题
选择数量在每一步隐式地考察所有可能的选择在每一步显式地做出一个选择
前提条件需要最优子结构 + 重叠子问题需要最优子结构 + 贪心选择性质
贪心选择性质不要求贪心选择性质必须满足贪心选择性质(局部最优选择能导致全局最优)
最优性保证总能得到最优解(前提是正确建模)仅在满足贪心选择性质时保证最优解
时间复杂度通常较高(需填表, 或更高)通常较低( 或更低)
空间复杂度通常需要 或更多空间存储子问题解通常只需 额外空间
适用范围广泛(几乎所有具有最优子结构的问题)较窄(需要额外满足贪心选择性质)

关键差异

1. 求解顺序的本质区别

这是两种方法最根本的区别:

  • 动态规划:采用自底向上的策略。先求解所有子问题,将结果存入表格,然后逐步组合子问题的解来构建原问题的解。在每一步,算法”回顾”已求解的子问题来做出最优选择。
  • 贪心算法:采用自顶向下的策略。在每一步做出当前看起来最好的选择(局部最优),然后求解剩下的子问题。做出选择后不需要回头。

2. 贪心选择性质是分水岭

贪心选择性质(Greedy-Choice Property)是指:可以通过做出局部最优(贪心)选择来产生全局最优解。

  • 如果问题满足贪心选择性质,贪心算法和动态规划都能得到最优解,但贪心算法通常更高效
  • 如果问题不满足贪心选择性质,贪心算法可能得到非最优解,此时必须使用动态规划

3. 经典问题分类

问题类型动态规划贪心算法
最长公共子序列 (LCS)
0-1 背包问题
矩阵链乘法
活动选择问题✓(也可用 DP)✓(更优)
Huffman 编码
最小生成树✓(Prim/Kruskal)
单源最短路径(非负权)✓(Dijkstra)
分数背包问题
最优二叉搜索树

判断方法

判断一个问题能否用贪心算法求解,关键在于验证贪心选择性质:是否存在一种贪心策略,使得局部最优选择能导致全局最优?如果无法确定,优先考虑动态规划。

4. 设计范式对比

动态规划的设计步骤

  1. 刻画最优解的结构特征(最优子结构)
  2. 递归定义最优解的值
  3. 计算最优解的值(通常自底向上填表)
  4. 构造最优解(回溯表格)

贪心算法的设计步骤

  1. 将最优化问题转化为这样的形式:先做出选择,再求解一个子问题
  2. 证明问题具有贪心选择性质
  3. 证明最优子结构
  4. 设计递归贪心算法(可转化为迭代算法)

参见