在做区间动态规划(Interval DP)时,很多人都会被一个问题困扰:
为什么循环一定要"这么写"?
为什么不能随便从前往后遍历?
这篇文章我们围绕一个经典区间 DP 场景(如 Burst Balloons)来系统讲清楚:
- 两种常见遍历方式
- 它们为什么都正确
- 为什么有些写法一定是错的
- 如何真正理解"逐个确定区间"
一、区间 DP 的本质
区间 DP 的核心状态通常是:
rec[i][j]
表示区间 (i, j)(通常是开区间)内的最优解。
关键问题只有一个:
当我们计算
rec[i][j]时,它依赖的所有更小区间必须已经算好。
这就是区间 DP 唯一的"铁律"。
二、写法一:左端点倒着走(i 从大到小)
代码结构:
python
for i in range(n - 1, -1, -1): # 左端点 i 往左挪
for j in range(i + 2, n + 2): # 右端点 j 往右扩
for k in range(i + 1, j):
# 计算 rec[i][j]
它的逻辑是什么?
可以这样理解:
- 先固定右边
- 再不断把左边往左拉
- 区间逐渐变大
举个直观例子:
假设我们要算 rec[0][4],它需要:
rec[0][2]rec[2][4]
而按照这种遍历方式:
rec[2][4]在i=2时就已经算好rec[0][2]在i=0且j=2时算好
所以当我们真正计算 rec[0][4] 时:
所有子区间都已经准备完毕。
这种写法的思维方式
它不是按"区间长度"思考,而是:
从右往左构造所有可能的区间。
更偏向"构造型思维"。
三、写法二:按区间长度从小到大
代码结构:
python
for length in range(2, n + 2):
for i in range(0, n + 2 - length):
j = i + length
for k in range(i + 1, j):
# 计算 rec[i][j]
这种写法更直觉
它的逻辑是:
- 先算长度为 2 的区间(最小合法开区间)
- 再算长度为 3
- 再算长度为 4
- ......
等所有短区间都算完了,再算长区间。
这是"层级式扩展"
你可以想象成这样:
长度 = 2 → 全部算完
长度 = 3 → 全部算完
长度 = 4 → 全部算完
...
每一层都建立在上一层之上。
四、为什么这两种方式都正确?
因为它们都满足了区间 DP 的核心条件:
当你计算一个大区间时,它内部所有更小区间都已经计算完成。
方法一保证方式
通过:
- i 倒序
- j 正序
保证:
- 右侧子区间提前算好
- 左侧子区间在当前 i 下较小 j 时已经算好
方法二保证方式
通过:
- length 从小到大
保证:
- 所有短区间先被计算
- 长区间只依赖短区间
五、为什么不能 i 和 j 都正序?
很多人一开始会写成这样:
python
for i in range(0, n):
for j in range(i + 2, n + 2):
# 计算 rec[i][j] ❌
看似合理,其实致命。
错在哪?
假设现在:
i = 0
j = 10
要计算 rec[0][10]。
它可能会用到:
rec[5][10]
但问题是:
- i 现在才到 0
- 还没走到 5
rec[5][10]根本没算
就像:
零件还没造好,就想组装整机。
这就是区间 DP 中最常见的错误。
六、真正理解"逐个确定区间"
很多人理解错了"逐个确定"。
它不是:
随便选一个区间算。
而是:
在一个合法顺序下,系统地确定每个区间。
区间不能随机确定
你不能:
- 先算
(0, 10) - 再算
(2, 5)
因为大区间依赖小区间。
必须有序
你必须选择一种拓扑顺序:
方案 A:按长度递增
小 → 大
方案 B:左端点倒序
右 → 左
七、两种方式如何选择?
| 方式 | 优点 | 适合人群 |
|---|---|---|
| 左端点倒序 | 写法简洁 | 熟悉区间 DP 的人 |
| 按长度递增 | 思维清晰 | 初学者更容易理解 |
如果你在做:
- 区间合并
- 石子合并
- 戳气球(Burst Balloons)
- 区间划分问题
推荐优先使用:
按长度递增
因为它更符合"由小到大构建"的直觉。
八、区间 DP 的终极理解
区间 DP 本质上是在构造一个 DAG(有向无环图):
- 每个区间是一个节点
- 小区间 → 大区间 是依赖关系
你必须找到一个合法的拓扑排序。
两种写法本质上都是:
对区间 DAG 进行合法的拓扑遍历。
九、一句话总结
区间 DP 的核心不是"怎么循环",而是:
如何保证在计算
rec[i][j]时,它依赖的所有子区间都已经计算完毕。
实现这一点的方法只有两类:
- 按区间长度从小到大
- 按左端点从右往左
记住这条铁律,区间 DP 就彻底通透了。