算法与数学基础 · 第 2 期

动态规划:把大问题 拆成小问题

记忆化 vs 递推 · dynamic programming step-by-step

算 fib(40) 暴力递归要算 约 1 亿次,动态规划只算 40 次。差别不在算法聪明,而在重复的子问题只算一次。DP 的本质是给递归加一张表,把指数级的重复计算压成线性。
⏱ 约 10 分钟 🎯 知道递归但没听过 DP 的人 📦 源:OI-wiki · DP 入门

01一个反共识:DP 不是新算法,是递归加张表

很多人觉得动态规划很玄,其实它就是"递归 + 记忆化"。同一个子问题第二次被问到时,直接查表返回,不再往下递归。

fib(n) = fib(n−1) + fib(n−2)

暴力递归算 fib(5) 时,fib(2) 被算了 3 次,fib(3) 被算了 2 次。n 一大,重复呈树形爆炸——fib(40) 的递归树有约 1.65 亿个节点,其中 fib(2) 一个就被算了约 3900 万次。DP 把这棵树压成一条线,每个 fib(k) 只算一次。

DP 不是新算法,
是递归加张表。
灏天文库 · 算法与数学基础 P.08

02DP 步骤点亮:自顶向下 vs 自底向上

选问题、设 n、点"下一步",看每个子问题被"点亮"的顺序。自顶向下从 fib(n) 往下递归(带记忆化),自底向上从 fib(0) 往上推。

🪜 DP 步骤点亮演示
观察两种 DP 走法的子问题点亮顺序与计算次数。
未计算 正在算 已存表 递归路径
点"下一步"开始。

03什么时候该用 DP:重叠子问题 + 最优子结构

不是所有递归都该改成 DP。DP 有两个前提:

前提一

重叠子问题

同一个子问题被多次计算。fib 满足,归并排序不满足(每个子数组只排一次)。

前提二

最优子结构

大问题的最优解由小问题的最优解组合而成。最短路满足,最长路径不一定。

典型

背包 / 编辑距离 / LIS

状态 = (i, j),转移 = 从几个子状态取最优。表格法直接套。

反例

分治 / 贪心

子问题不重叠用分治;局部最优即全局最优用贪心,不必存表。

判断顺序:先看能不能拆成子问题,再看子问题重不重叠,最后看是不是求最优/计数。三个都满足,DP 就是首选。

重叠的子问题,
只该算一次。
灏天文库 · 算法与数学基础 P.10

04带走这套清单

✅ DP 5 条可执行规则

  1. 先写暴力递归,再加记忆化表,比一上来就推状态转移方程容易。
  2. 自顶向下好理解,自底向上省栈空间,按需选。
  3. 状态定义要"完整描述子问题",背包要带容量 j,不能只带 i。
  4. 转移方程写完后,画一遍小例子验证,别直接交。
  5. 能用滚动数组压空间的就压,O(n²) 空间常能压成 O(n)。
递归加张表,
指数变线性。
灏天文库 · 算法与数学基础 P.11