zoukankan      html  css  js  c++  java
  • 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)。求方案数。

    题解:

    经典的树上 ( ext{DP}) + ( ext{DP}) 优化 ( ext{DP})

    考虑 (f_{i, j, 0/1/2}) 表示以 (i) 为根的子树,选了 (j) 个特殊颜色,其中节点 (i) 的颜色小于/等于/大于 (k) .

    这个转移的话肯定是有子树转移而来,(2) 个子树很好处理,相乘即可。但是多个子树呢?

    就不那么好处理了,复杂度会很高。

    所以我们考虑再用一个新的 ( ext{DP}) 去更新这个 ( ext{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} imes sum^{2}_{s = 0}{f_{v, k, s}})} ]

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

    [g_{i, j, 2} = sum^{j}_{k = 0}{(g_{i - 1, j - k, 2} imes 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;
        }
    }
    
  • 相关阅读:
    file
    shell脚本的执行
    添加源,删除源
    lsattr, chattr
    umask
    od
    init
    sync
    wc
    history
  • 原文地址:https://www.cnblogs.com/Flash-plus/p/13834134.html
Copyright © 2011-2022 走看看