Skip to content

方法Method

动态规划

Dynamic programming

在有限或良基的状态依赖上复用已计算结果的算法设计范式。

形式陈述 ​

动态规划把序列递推推广为状态集 S 上的方程:每个 s∈S 的值只由其直接依赖 pred(s) 的值组合而成,并为无依赖状态给出基例。若依赖关系是有限无环图,就用拓扑排序确定填表次序;也可从目标状态递归展开,用记忆化保存每个已解状态。更一般地,依赖可以由严格关系 ≺ 给出:若 t∈pred(s) 时总有 t≺s,且 ≺ 是良基的,则可用良基递归确定每个状态值。要得到有限算法,还需目标可达的依赖图有限,每个状态的直接依赖可有效枚举,基例与组合运算能在有限时间完成。良基性本身允许无限多个直接依赖,不能替代这些计算条件。

优化问题常有

F(s)=opta∈A(s){c(s,a)+F(T(s,a))},

这里 A(s) 是状态 s 允许的选择,c(s,a) 是这一步的贡献,T(s,a) 是选择后所依赖的较小状态;先固定这些含义,才有依据决定用最小值还是最大值合并。计数、可达性、概率传播和字符串识别使用相应的加法、逻辑或等运算,并不需要“最优子结构”。若有 N 个可达状态,每个状态检查至多 T 个转移,每次转移、查表及一个状态的初始化和存储均为常数成本,则直接实现需 O(N(T+1)) 时间和 O(N) 存储。大整数计数不能忽略单次加法的位成本。

若状态依赖含环,单纯按表格顺序代入已无定义。一种严格的扩展是先给状态值域配备偏序,再把全部状态方程写成单调算子 F,并明确规定要取的解,例如最小不动点 lfp(F);还需为所用迭代证明收敛到该不动点。任意环状递推方程不会因为“可以反复更新”就自动成为动态规划:解可能不存在、不唯一,或更新根本不收敛。

直觉

动态规划先问“未来究竟需要哪些过去信息”,再把这份充分摘要定义为状态。当两条路径得到同一状态时,它们的后续计算便可合并。状态太少会丢失影响未来的信息,太多则把整段历史原样带入。因此核心工作是证明状态充分、转移完备,并给出合法求值顺序。

动态规划状态依赖与填表顺序
例子与边界

动态规划主动识别并合并相同子问题,以状态表或记忆化避免重复计算;回溯法沿选择树枚举候选,并靠可行性或界限剪枝撤销选择。两者可以组合,但“存在递归分支”并不足以构成动态规划。

Fibonacci 递归的调用树重复解同一下标,记忆化后只剩 O(n) 个状态。最长公共子序列用两个前缀长度 (i,j) 作状态,因而有 (m+1)(n+1) 个前缀状态。0–1 背包可定义 dp[i,w] 为前 i 件物品在容量 w 下的最大价值。设第 i 件重量为 ai、价值为 vi,当 w≥ai 时,转移为 dp[i,w]=max{dp[i−1,w],dp[i−1,w−ai]+vi};第一项是不选,第二项是选一次,两者都只能读取上一行。

只考虑一件重量 2、价值 3 的物品,容量为 4,初始一维表全零。若容量从小到大更新,先把 dp[2] 设为 3,再用这个刚写入的值计算 dp[4]=dp[2]+3=6,相当于把物品选了两次。从大到小更新时,计算 dp[4] 读到的 dp[2] 仍是上一行的 0,结果为 3;之后才改 dp[2]。逆序不是口诀,而是在原地存储中保留递推依赖的旧版本。

若还要输出选中的物品,仅存最优数值不够;可在二维表记录每个状态采用哪条转移,再从目标状态沿选择倒推。压缩空间后这些历史选择可能被覆盖,需要另存信息或重新计算,不能把数值计算的空间界直接当成完整解重建的空间界。

编辑距离网格把这一区别具体化:完整前缀表能回溯插删替换脚本,滚动行只保留费用。Hirschberg 方法先用前后向费用认证中线切口,再释放数组、递归重建;因此要分别核算工作数组、递归栈和输出。

状态设为“当前价值”却不保留已用容量,通常无法决定背包的后续可行性;反过来,把所有已选集合当作状态虽然充分,却退化为指数枚举。“写出了递推式”只是起点;还必须证明状态确实包含后续所需的全部信息,转移覆盖了所有合法情形,且所选求值规则真能得到指定的解。

推论与应用

记忆化以需求驱动方式解状态,表格法则按依赖顺序主动求值。动态规划可用于 DAG 最短路、序列比对、背包、矩阵链乘、区间问题、树分解、概率模型与有限时域控制。其中计数、概率与可达性例子也说明,动态规划的主体是状态方程的有效求值,而不是优化问题所特有的“最优子结构”。它与分治的分界是是否复用重叠状态,与贪心的分界则是是否保留多种中间可能后再比较。

高级 DP 仍应从状态 DAG 看待,而不是把新技巧当模板名。子集动态规划把 2n 个子集作为节点,树宽 DP把 bag 边界状态沿树分解传播,Color Coding用随机着色压缩“互异顶点”约束;三者分别以指数状态、结构参数和随机化换可解性。四俄罗斯与字级并行则批量执行一组有限转移,只改善每条边的处理成本,不改变状态依赖正确性。

另一些加速减少的是候选转移。Monge 四点不等式可证明最左切点单调,分治 DP 优化再据此收紧搜索范围;若转移还能展开为直线查询,直线下包络利用斜率与查询顺序维护候选。每一种省略都要对应自己的结构证明。

参考资料
  • MIT 6.006,Lecture 16: Dynamic Programming Subproblems,2020,课程讲义:状态定义、递推关系、求值顺序与解重建。

  • Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022,Part IV, Ch. 14 “Dynamic Programming”。

  • Jon Kleinberg and Éva Tardos, Algorithm Design, Pearson, 2005,Ch. 6, dynamic programming and optimal substructure。

关系图谱25 个相邻概念 · 4 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
分类位置

上位 / 更一般

暂未标注直接上位概念。

下位 / 直接特例

类型化关系