🧠 第六部分:动态规划

竞赛编程中最强大也最需要训练的主题。掌握记忆化、递推、经典模型、折半枚举与数位计数,逐步跨过 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、树形 DPSilver/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
二维 DP6.20/1 背包、网格路径明确每一维代表什么
LIS6.2O(N²) 与 O(N log N) LIStails 数组不是实际 LIS
状压 DP6.3TSP、任务分配mask 表示集合状态
区间 DP6.3矩阵链乘法、合并石子按区间长度递增填表
树形 DP6.3树上独立集后序遍历先算子树
折半搜索6.4子集和、最大不超过 X 的子集和2^40 不可做,两个 2^20 可做
数位 DP6.5不含某数字、数字和、整除性tightstarted 是核心

前置条件

开始第六部分前,请确认你能做到:

  • 编写递归函数并理解调用栈(第 2.3 章)
  • 熟练使用数组、二维向量和简单结构体(第 2.3 章)
  • 理解排序、二分查找和 lower_bound / upper_bound(第 3.3 章)
  • 理解位运算与 bitmask 枚举(第 2.6 章)
  • 能解决基础 DFS/BFS 题目(第 5.2 章)——DP 和图搜索都在探索状态空间

DP 思维方式

DP 不是死记公式,而是反复问四个问题:

  1. 状态是什么? 描述一个子问题需要哪些信息?
  2. 转移是什么? 当前状态能从哪些更小或更简单的状态得到?
  3. 初始条件是什么? 哪些状态不用计算就知道答案?
  4. 计算顺序是什么? 依赖必须先于被依赖者计算。

💡 核心思路: 如果暴力搜索中同一个子问题被反复计算,DP 就是把它缓存起来;如果暴力枚举太大但可以分成两半独立处理,折半搜索可能比 DP 更自然;如果范围大到不能枚举每个整数,但限制只和数字有关,数位 DP 往往是答案。


本部分学习建议

  1. 第 6.1 章不要跳。 你要真正理解“状态”和“转移”,而不是只记代码。
  2. 每个经典题至少写两遍。 一遍记忆化搜索,一遍递推填表;两种视角互相验证。
  3. 第 6.2 章重点练背包和 LIS。 它们是很多更难题的底层模型。
  4. 第 6.3 章开始进入 Silver/Gold。 状压、区间、树形 DP 都需要更强的状态设计能力。
  5. 第 6.4 章不是传统 DP,但和 DP 同样重要。 当你看到 N≤40 和子集枚举时,应立即想到折半搜索。
  6. 第 6.5 章要画状态。 数位 DP 的 tightstartedpos 最容易混,建议用小数字手工追踪。
  7. 所有 DP 都要检查边界。 空集、0、负数、L=0、数组维度、INF 溢出,都是常见陷阱。

⚠️ 警告: DP 第 1 号 bug:在最小化 DP 中使用 dp[w-c] 前忘记检查 dp[w-c] != INFINF + 1 会溢出!

DP 第 2 号 bug:0/1 背包 vs 完全背包的循环顺序搞错了。倒序迭代 = 每件物品最多用一次;正序迭代 = 无限次使用。

Gold DP 第 1 号 bug:状态维度缺了一维。比如数位 DP 少了 tightstarted,答案通常会在边界样例上错。