algo12 动态规划(下)
进阶 DP 专题——区间 DP、树形 DP、状态压缩 DP、数位 DP 与优化入门
一、区间 DP
1.1 核心思想
区间 DP 处理的是定义在区间
通用模板:
# 按区间长度递增枚举
for length in range(2, n + 1): # 枚举区间长度
for l in range(0, n - length + 1): # 枚举左端点
r = l + length - 1 # 计算右端点
for k in range(l, r): # 枚举分割点
dp[l][r] = merge(dp[l][k], dp[k+1][r])1.2 矩阵链乘法(Matrix Chain Multiplication)
问题:给定
状态:
转移:
其中
复杂度:
1.3 石子合并问题
问题:
状态:
转移:
前缀和
1.4 最优二叉搜索树(Optimal BST)
给定
![区间DP示意图:左侧展示矩阵链乘法的括号化过程——[A1 A2 A3] 可以被拆分为 (A1)(A2 A3) 或 (A1 A2)(A3);中间展示石子合并的区间合并过程——3堆{4,3,2}先合并{3,2}成5,再与4合并;右侧展示DP表填充顺序——对角线到右上角](/learn-ai/assets/algo12-01-interval-dp.88VYCU0a.png)
二、树形 DP
2.1 树形 DP 的特点
树形 DP 将 DP 定义在树的节点上,利用树的后序遍历(先子节点后父节点)来保证计算顺序。通用模式:
def dfs(u, parent):
for v in adj[u]:
if v != parent:
dfs(v, u) # 先计算子节点
# 用子节点的 dp 值更新父节点的 dp 值2.2 树的最大独立集
问题:在树中选出尽可能多的节点,使得任意两个选中的节点不相邻。
状态:
:不选节点 时,以 为根的子树能选的最大节点数 :选节点 时,以 为根的子树能选的最大节点数
转移:
2.3 树的直径
树的直径(Diameter):树中任意两点间最长简单路径的长度(边数或边权和)。
解法一(双 DFS/BFS):从任意点出发找到最远点
解法二(树形 DP):
- 对于节点
,维护以 为根的子树中,从 向下延伸的最长路径 max_depth[u] - 对于每个节点
,以其为"最高点"的直径候选为 depth1 + depth2(两条最长向下路径之和) - 全局直径 =
(过 的直径候选)
2.4 树上的背包
将 0-1 背包的思想放到树上:每个节点是一件物品,选了子节点才能选父节点(依赖关系)。DFS 遍历时,在每个节点上做一次"分组背包"合并子树的 DP 值。
![树形DP示意图:左侧为一棵树,节点标注编号;中间展示后序遍历顺序(箭头从子节点指向父节点);右侧展示dp[u][0]和dp[u][1]的计算示例——红色节点表示"被选中",绿色节点表示"未选中",最大独立集大小用数字标注在根节点旁](/learn-ai/assets/algo12-02-tree-dp.B96orQej.png)
三、状态压缩 DP
3.1 核心思想
当问题的状态空间较小(通常
3.2 旅行商问题(TSP: Held-Karp 算法)
问题:给定
状态:mask,最后访问的城市是
转移:
其中 mask \ {i} 表示从 mask 中移除城市
复杂度:
3.3 位运算速查
| 操作 | 含义 | 代码 |
|---|---|---|
| 检查第 k 位 | i 是否在 mask 中 | (mask >> k) & 1 |
| 设置第 k 位 | 将 i 加入 mask | mask | (1 << k) |
| 清除第 k 位 | 将 i 从 mask 中移除 | mask & ~(1 << k) |
| 遍历所有子集 | 枚举 mask 的所有子集 | sub = (sub - 1) & mask |
| 集合大小 | mask 中 1 的个数 | bin(mask).count('1') |
四、数位 DP
4.1 核心思想
数位 DP 用于统计在区间
4.2 通用框架
统计
然后对单个上界
def count_up_to(N):
# 将 N 转为字符串,方便逐位处理
digits = list(map(int, str(N)))
# dp[pos][tight][...状态...]
# pos: 当前处理到第几位
# tight: 是否受上界 N 的约束(tight=1 表示当前前缀等于 N 的前缀)4.3 经典问题:统计不含 4 的数字个数
统计
pos:当前位tight:是否紧贴上限- (无需额外状态,因为只检查是否出现 4)
4.4 经典问题:数字和
统计 pos 位的数字和。

五、DP 优化入门(概念介绍)
5.1 凸包技巧(Convex Hull Trick, CHT)
对于形如
适用范围:斜率单调的 DP 转移(如 1D/1D DP 的分治优化)。
5.2 分治优化
对于转移形式
5.3 四边形不等式(概念)
若成本函数
本章总结
| 概念 | 一句话 |
|---|---|
| 区间 DP | 按区间长度递增,枚举分割点,状态 |
| 矩阵链乘法 | |
| 树形 DP | 后序遍历,子节点结果聚合成父节点 |
| 最大独立集 | |
| 树直径 | 最长向下路径 + 次长向下路径 |
| 状态压缩 DP | 用二进制位表示集合, |
| 数位 DP | 逐位枚举 + 记忆化搜索,统计 |
| CHT / 斜率优化 | 将特定形式 DP 从 |
区间 DP、树形 DP、状态压缩 DP 和数位 DP 各有其独特的适用场景和设计模板。掌握它们的关键是识别问题的结构性特征——区间合并?树的后序遍历?集合的二进制表示?数字的逐位枚举?——一旦匹配到合适的模板,剩下的就是填表的"体力活"了。
📥 Code
| File | View | Download |
|---|---|---|
| demo.py | Open | Download |
| exercise.py | Open | Download |
参考
- Cormen, T. H. et al. (2009). Introduction to Algorithms (3rd ed.). MIT Press. Chapter 15.
- Held, M. & Karp, R. M. (1962). A dynamic programming approach to sequencing problems. Journal of the Society for Industrial and Applied Mathematics, 10(1), 196-210. [doi:10.1137/0110015]
- Bellman, R. (1962). Dynamic programming treatment of the travelling salesman problem. Journal of the ACM, 9(1), 61-63.