DP 状态设计
这是一篇总结了小部分 DP 状态设计的总结。
1. 可以交换值域和dp状态的某个维度
简单来说,就是把自己要求的放到其中的一维,把自己存不下的一维变成 dp 记录的东西,最后靠枚举来取得答案。
适用范围:要求答案很小但其中一维范围很大。
我们先来举个例子。
例题 atcoder dp_e
对于这道题,第一眼肯定是用 01背包 去做,但我们观察到 \(1 \le W \le 10^9\),所以按照正常的背包肯定做不了。
但是我们有注意到他的价值非常的小仅有 \(10^5\) 所以我们定义状态为:\(dp_{i,j}\) 表示枚举到第 \(i\) 个物品时,总价值为 \(j\) 的方案的最小重量。我们可以像 01背包 一样压缩成一维,也就是 \(dp_i\) 表示总价值为 \(j\) 的方案的最小重量。
最后从大往小枚举符合条件的价值就行了。
戳我看代码喵~
int n, m;
int dp[N];
int w[N], v[N];
signed main() {
n = re, m = re;
int M = 0;
for (int i = 1; i <= n; i++) w[i] = re, v[i] = re, M += v[i];
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i++) for (int j = M; j >= v[i]; j--) dp[j] = min(dp[j], dp[j - v[i]] + w[i]);
for (int i = M; i; i--) if (dp[i] <= m) wr(i), endl, exit(0);
}
2. 位置型 dp
一般状态表示
\(dp_i\) 表示前 \(i\) 个 / 第 \(i\) 个...
\(dp_{i, j}\) 表示第 \(i\) 行 \(j\) 列...
\(dp_{l, r}\) 表示区间 \([l, r]\) 内...
\(dp_u\) 表示以子树 \(u\) 为根...
总结一下
设计一个好的 dp 有以下几步:
- 先写一个一定正确的暴力状态
- 指出它的后效性或复杂度瓶颈
- 找哪些历史对未来等价,删掉无关维度
- 证明同状态历史对所有未来转移完全相同
- 最后再考虑如何优化转移

浙公网安备 33010602011771号