🧠 第六部分:动态规划
竞赛编程中最强大也最需要训练的主题。掌握记忆化、递推、经典模型、折半枚举与数位计数,逐步跨过 USACO Silver 到 Gold 的门槛。
📚 5 章 · ⏱️ 预计 4-6 周 · 🎯 目标:USACO Silver → Gold
第六部分:动态规划
预计用时:4-6 周
动态规划不是背公式,而是把一个大问题拆成许多会重复出现的小问题。你需要学会定义状态、设计转移、处理边界,并判断什么时候 DP 不是最好的工具。本部分从最基础的一维 DP 开始,逐步过渡到背包、LIS、状压、区间、树形、折半搜索和数位 DP。
涵盖的主题
| 章节 | 主题 | 核心思想 | 目标层级 |
|---|---|---|---|
| 第 6.1 章 | DP 入门 | 记忆化、递推、DP 四步法 | Bronze/Silver |
| 第 6.2 章 | 经典 DP 问题 | LIS、背包、网格路径、方案数 | Silver |
| 第 6.3 章 | 进阶 DP 模式 | 状压 DP、区间 DP、树形 DP | Silver/Gold |
| 第 6.4 章 | 折半搜索 | 将 2^N 拆成两个 2^(N/2) 再合并 | Gold |
| 第 6.5 章 | 数位 DP | 按位填数,统计 [L, R] 内满足数字性质的数 | Gold |
学完本部分后能解决什么问题
完成第六部分后,你将能够挑战:
-
USACO Bronze:
- 简单计数问题(做某件事有多少种方法?)
- 基本优化问题(最小代价、最大收益)
-
USACO Silver:
- 最长递增子序列及其变体
- 0/1 背包、完全背包、二维费用背包
- 网格路径计数与最大价值路径
- 需要精确定义状态的一维/二维 DP
-
USACO Gold:
- 状压 DP、区间 DP、树形 DP
N≈40的指数枚举题:使用折半搜索把复杂度减半[L, R]内数字性质计数题:使用数位 DP 处理10^18级别范围- 多状态组合:数字和、余数、上一位、出现次数、上下界约束
需要掌握的关键 DP 模式
| 模式 | 章节 | 示例题目 | 关键提醒 |
|---|---|---|---|
| 一维 DP(顺序) | 6.1 | 斐波那契、爬楼梯 | 先写状态含义,再写转移 |
| 一维 DP(优化) | 6.1 | 硬币找零(最少硬币) | 最小值初始化为 INF |
| 一维 DP(计数) | 6.1 | 硬币找零(方案数) | 初始方案数通常是 dp[0]=1 |
| 二维 DP | 6.2 | 0/1 背包、网格路径 | 明确每一维代表什么 |
| LIS | 6.2 | O(N²) 与 O(N log N) LIS | tails 数组不是实际 LIS |
| 状压 DP | 6.3 | TSP、任务分配 | mask 表示集合状态 |
| 区间 DP | 6.3 | 矩阵链乘法、合并石子 | 按区间长度递增填表 |
| 树形 DP | 6.3 | 树上独立集 | 后序遍历先算子树 |
| 折半搜索 | 6.4 | 子集和、最大不超过 X 的子集和 | 2^40 不可做,两个 2^20 可做 |
| 数位 DP | 6.5 | 不含某数字、数字和、整除性 | tight 与 started 是核心 |
前置条件
开始第六部分前,请确认你能做到:
- 编写递归函数并理解调用栈(第 2.3 章)
- 熟练使用数组、二维向量和简单结构体(第 2.3 章)
-
理解排序、二分查找和
lower_bound/upper_bound(第 3.3 章) - 理解位运算与 bitmask 枚举(第 2.6 章)
- 能解决基础 DFS/BFS 题目(第 5.2 章)——DP 和图搜索都在探索状态空间
DP 思维方式
DP 不是死记公式,而是反复问四个问题:
- 状态是什么? 描述一个子问题需要哪些信息?
- 转移是什么? 当前状态能从哪些更小或更简单的状态得到?
- 初始条件是什么? 哪些状态不用计算就知道答案?
- 计算顺序是什么? 依赖必须先于被依赖者计算。
💡 核心思路: 如果暴力搜索中同一个子问题被反复计算,DP 就是把它缓存起来;如果暴力枚举太大但可以分成两半独立处理,折半搜索可能比 DP 更自然;如果范围大到不能枚举每个整数,但限制只和数字有关,数位 DP 往往是答案。
本部分学习建议
- 第 6.1 章不要跳。 你要真正理解“状态”和“转移”,而不是只记代码。
- 每个经典题至少写两遍。 一遍记忆化搜索,一遍递推填表;两种视角互相验证。
- 第 6.2 章重点练背包和 LIS。 它们是很多更难题的底层模型。
- 第 6.3 章开始进入 Silver/Gold。 状压、区间、树形 DP 都需要更强的状态设计能力。
- 第 6.4 章不是传统 DP,但和 DP 同样重要。 当你看到
N≤40和子集枚举时,应立即想到折半搜索。 - 第 6.5 章要画状态。 数位 DP 的
tight、started、pos最容易混,建议用小数字手工追踪。 - 所有 DP 都要检查边界。 空集、0、负数、
L=0、数组维度、INF溢出,都是常见陷阱。
⚠️ 警告: DP 第 1 号 bug:在最小化 DP 中使用
dp[w-c]前忘记检查dp[w-c] != INF。INF + 1会溢出!DP 第 2 号 bug:0/1 背包 vs 完全背包的循环顺序搞错了。倒序迭代 = 每件物品最多用一次;正序迭代 = 无限次使用。
Gold DP 第 1 号 bug:状态维度缺了一维。比如数位 DP 少了
tight或started,答案通常会在边界样例上错。