#QBXT2020 3月DP营 Day1下午

区间动态规划

合并果子

有n对石子,可以选择相邻的两堆,付出的代价的是两堆石子之和,把n堆石子合并到一堆最小的代价是多少。

f[l][r] 表示第l到第r堆合并到一起需要的体力值。

\[f[i][j] = min(f[l][k] + f[k+1][r],f[i][j]) + sum[i][j]; \]

for(int i = 1;i <= n; i++)
    f[i][i] = 0;
for(int len = 2;len <= n; len++){
    for(int l = 1,r = len;r <= n; l++,r++){
        for(int k = l;k < r; k++){
            f[l][r] = min(f[l][r],f[l][k] + f[k+1][r] + sum[l~r])
        }
    }
}

环形合并石子

环形里面总有一条边是用不上的,从那个边断开成链,再按上面的做。枚举断点。或者做双倍DP。

括号匹配

POJ2955

给出一个的只有()[]四种括号组成的字符串,求最多能够选出多少个括号满足完全匹配。

f[l][r]表示从l到r的答案。

f[i][j] = f[i][k] + f[k+1][j] +

再加一个什么?这个不像之前做过的题,要把所有跨区间的加上,比如下面的:

\[[][][[][]] \ \ \ 或 \ \ \ [[[[[]]]]][] \]

以上的操作只需要通过正确枚举区间断点即可解决,真正需要特别考虑的是一下的情况:

\[[ \ [[[[[[[]]]]]]]] \ \ \ 或 \ \ \ [[][][][[[]]]] \]

这里我们只需要特别考虑区间左右端点上面的括号匹配就可以。因为枚举断点的时候分开的区间都不能为空,所以这是需要且只需要考虑的情况。

删数字

POJ1651

f[l][r] = f[l][k] + f[k][r] + a[l] * a[k] * a[r]

回文子序列

给定字符串,求回文子序列的数量。

f[l][r]表示l到r的数量。要避免算重算漏两种情况。

没话说 牛逼

\[f[l][r] = f[l][r-1] + f[l+1][r] - f[l+1][r-1] + (s[l] == s[r]) * f[l+1][r-1] \]

多边形

给定凸四边形,每个点有一个权值,将多边形三角化,每个三角形的三个点权乘起来在求和,求最小和。

f[l][r]。如果连两个不能构成三角形的端点,就相当于把区间分成了左右两部分。则状态转移:

\[f[i][j] = f[i][k] + t[k][j] + a[i] * a[k] * a[j] \]

背包

(背包九讲赶紧去看嗷嗷嗷嗷嗷)

基础问题

n个物品,m容量的包,,每个物品有体积和价值,最大化价值和。

1:可以使用无限次

就是01背包的扩展,体现在代码上就是正着枚举

2:可以使用有限次

①:01背包 f[i] = max(f[i],f[i-v[j]] + w[j])

f[0][0] = 0;
for(int i = 1;i <= n; i++){
    for(int j = 0;j <= m; j++){
        f[i][j] = f[i-1][j];
        if(j > v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]] + w[i]);
    }
}
for(int i = 0;i < n; i++){
    for(int j = 0;j <= m; j++){
        f[i+1][j] = max(f[i+1][j],f[i][j]);
        f[i+1][j+v[i+1]] = max(f[i+1][j+v[i+1]],f[i][j] + w[i]);
    }
}

一维写法:

for(int i = 1;i <= n; i++)
    for(int j = m;j >= v[i];j--)
        f[j] = max(f[j],f[j-v[i]] + w[i]);
  • 求最大价值的具体方案

pre[i][j]:假设是从f[i-1][k]转移到f[i][j]的,假如转换完之后体积总量没发生变化,就相当于没选,如果发生变化就是已经选了。

有限背包

第一种方法:f[i][j][k]表示i种物品j的体积用了k个

第二种方法:转化成01背包。

空间复杂度:\(M \times \sum k\)(M是体积),但是还是三次方。接下来看怎么降维优化。首先想到二进制拆分,把k进行二进制拆分,也就是把第j中物品的k件拆成2进制,不直接放入k个w[j]和v[j],而是放入\((w[j],v[j])、(2 \times w[j],2 \times v[j])、2^2 \times(w[j],v[j])……2^{\log _2 k} (w[j],v[j])\)

代码

v_[i],w_[i],k[i];//当前的背包
v[i],w[i];//原来的01背包
for(int i = 1;i <= n; i++){
    int r = 1;
    while(r <= k[i]>){
        cnt++;
        v[cnt] = v_[i] * r;
        w[cnt] = w_[i] * r;
        r <<= 1;
        k[i] -= r;
    }
    if(k[i] != 0){
        cnt++;
        v[cnt] = v_[i] * k[i];
        w[cnt] = w_[i] * k[i];
    }
}

多次询问删数

k组询问,每次询问\(p_i\)表示删除第\(p_i\)种背包之后的01背包问题。注意:每个询问相互独立。

\(n <= 10^5,M <= 100,k <= 1000\)

正着DP一遍,前i个物品的情况,倒着DP一遍,后i个物品的情况。

f[][]//正着
g[][]//倒着
for(int i = 1;i <= q; i++){
    cin >> k;
    for(int j = 0;j <= v[k]; j++){
        ans = max(ans,f[i-1][j] + f[i+1][v[k] - j]);//v是体积
    }
}
posted @ 2020-03-29 13:59  CYC的幸福生活  阅读(442)  评论(0编辑  收藏  举报