dp好题
1. CF813D
题意:
给一个长度为\(n\)的序列,求两个不相交的子集长度之和最大是多少,能放入同一子集的条件是首先顺序不能变,然后每一个相邻的要么相差\(1\)或者相差\(7\)的倍数。 \(n < 5000\)
题解:
\(f[i][j]\) 表示第一序列到了第 \(i\) 位,第二个序列到了第 \(j\) 位,符合条件的长度之和最大.
那么显然可以退出 \(O(n^3)\) 方程:
i : 1 ~ n
j : i + 1 ~ n
k : 分两段。
第一段:1 ~ i,此时判一下a[k]有没有被j选中,如果没有在考虑转移i
第二段:j ~ n,此时就分别转移i,j就行了
那么这肯定是过不了的,所以还需要优化。
就是开一个桶记录相差 \(1\) 的 \(max\)
再开一个桶记录 \(\%7\) 同余
代码:
Rep(j, 1, i - 1) pre[a[j]] = max(pre[a[j]], f[i][j]), num[a[j] % 7] = max(num[a[j] % 7], f[i][j]);
Rep(j, i + 1, n) { f[i][j] = f[i][0] + 1;
f[i][j] = max(f[i][j], max(pre[a[j] + 1] + 1, max(pre[a[j] - 1] + 1, num[a[j] % 7] + 1))); f[j][i] = f[i][j];
pre[a[j]] = max(pre[a[j]], f[i][j]), num[a[j] % 7] = max(num[a[j] % 7], f[i][j]); ans = max(ans, f[i][j]);
2.CF796E
题意:
有 \(n\) 道题目,有两个人分别会做某些题目,有 \(p\) 次偷看机会,每次可以偷看某个人最多连续 \(k\) 道题目,求最多偷看几道题目。
题解:
这题细节比较多,思路比较板。
具体 令\(dp[i][j][L][R]\) 表示当前为第 \(i\) 题,已经偷看了 \(j\) 次,还能向左边的大神看 \(L\) 道题,向右边的大神看 \(R\) 道题。
然后就随便转移(雾
代码
int now = f[(i + 1) % 2][j][L][R];
f[i % 2][j][max(L - 1, 0)][max(R - 1, 0)] = max(f[i % 2][j][max(L - 1, 0)][max(R - 1, 0)], now);
if(L) f[i % 2][j][L - 1][max(R - 1, 0)] = max(f[i % 2][j][L - 1][max(R - 1, 0)], now + a[i]);
if(R) f[i % 2][j][max(L - 1, 0)][R - 1] = max(f[i % 2][j][max(L - 1, 0)][R - 1], now + b[i]);
if(L) f[i % 2][j + 1][L - 1][k - 1] = max(f[i % 2][j + 1][L - 1][k - 1], now + (a[i] | b[i]));
if(R) f[i % 2][j + 1][k - 1][R - 1] = max(f[i % 2][j + 1][k - 1][R - 1], now + (a[i] | b[i]));
if(L && R) f[i % 2][j][L - 1][R - 1] = max(f[i % 2][j][L - 1][R - 1], now + (a[i] | b[i]));
f[i % 2][j + 1][k - 1][max(R - 1, 0)] = max(f[i % 2][j + 1][k - 1][max(R - 1, 0)], now + a[i]);
f[i % 2][j + 1][max(L - 1, 0)][k - 1] = max(f[i % 2][j + 1][max(L - 1, 0)][k - 1], now + b[i]);
f[i % 2][j + 2][k - 1][k - 1] = max(f[i % 2][j + 2][L - 1][R - 1], now + (a[i] | b[i]));
3.CF855C
题意:
给你一个树,可以染 \(m\) 个颜色,定义一个特殊颜色 \(k\) ,要求保证整棵树上特殊颜色的个数不超过 \(x\) 个。同时,如果一个节点是特殊颜色,那么它的相邻节点的颜色编号必须全部小于 \(k\)。求方案数。
题解:
经典的树上 \(\text{DP}\) + \(\text{DP}\) 优化 \(\text{DP}\)。
考虑 \(f_{i, j, 0/1/2}\) 表示以 \(i\) 为根的子树,选了 \(j\) 个特殊颜色,其中节点 \(i\) 的颜色小于/等于/大于 \(k\) .
这个转移的话肯定是有子树转移而来,\(2\) 个子树很好处理,相乘即可。但是多个子树呢?
就不那么好处理了,复杂度会很高。
所以我们考虑再用一个新的 \(\text{DP}\) 去更新这个 \(\text{DP}\) 。
设 \(g_{u, i, j, 0/1/2}\) 表示 \(u\) 的子树中考虑前 \(i\) 个儿子,选了 \(j\) 个 \(k\),节点 \(i\) 的状态是 \(0/1/2\) 的方案数。
那么这个也很好转移,即:
然后赋回给
但是这样写还不行,\(g\) 数组的空间会炸,所以把第二维滚掉即可。
代码
for(int j = 0; j <= x; ++ j) {
for(int kk = 0; kk <= j; ++ kk) {
g[qwq][now][j][0] = (g[qwq][now][j][0] + (g[qwq][now ^ 1][j - kk][0] * (f[v][kk][0] + f[v][kk][1] + f[v][kk][2]) % XRZ)) % XRZ;
g[qwq][now][j][1] = (g[qwq][now][j][1] + (g[qwq][now ^ 1][j - kk][1] * f[v][kk][0] % XRZ)) % XRZ;
g[qwq][now][j][2] = (g[qwq][now][j][2] + (g[qwq][now ^ 1][j - kk][2] * (f[v][kk][0] + f[v][kk][2]) % XRZ)) % XRZ;
}
}