博客园 首页 私信博主 显示目录 隐藏目录 管理

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_{i, j, 0} = \sum^{j}_{k = 0}{(g_{i - 1, j - k, 0} \times \sum^{2}_{s = 0}{f_{v, k, s}})} \]

\[g_{i, j, 1} = \sum^{j}_{k = 0}{(g_{i - 1, j - k, 1} \times f_{v, k, 0})} \]

\[g_{i, j, 2} = \sum^{j}_{k = 0}{(g_{i - 1, j - k, 2} \times \sum^{}_{s = 0, 2}{f_{v, k, s}})} \]

然后赋回给

\[f_{u, j, 0 / 1 / 2} = g_{u, i, j, 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;
    }
}
posted @ 2020-10-18 11:03  Flash_plus  阅读(147)  评论(0编辑  收藏  举报