Codeforces392E - Deleting Substrings - 动态规划

392E - Deleting Substrings

分类: dp

题意: 有一长度为 n   ( 1 ≤ n ≤ 400 ) n\ (1\leq n\leq 400) n (1n400) 的序列 W   ( 1 ≤ w i ≤ 1 0 9 ) W\ (1\leq w_i\leq 10^9) W (1wi109) V   ( 0 ≤ ∣ v i ∣ ≤ 2000 ) V\ (0\leq |v_i|\leq 2000) V (0vi2000) ,对于它的一段子序列 w l w_l wl w l + 1 w_{l+1} wl+1 、…、 w r w_r wr, 如果满足 ∣ w i − w i + 1 ∣ = 1 |w_i-w_i+1|=1 wiwi+1=1 2 ⋅ w i − w i + 1 − w i − 1 ≥ 0 2·w_i-w_{i+1}-w_{i-1}≥ 0 2wiwi+1wi10 ,那么你可以将这一段删除,并获得 v r − l + 1 v_{r-l+1} vrl+1 的价值,任意时刻都可以停止操作(没删完也行),现在问你获得的最大价值和是多少?

题解: 显然能删的就是连续的一段上升、下降或者锯齿区间,不妨考虑动态规划, f ( i , j ) f(i,j) f(i,j) 表示把区间 [ i , j ] [i,j] [i,j] 删除的最大价值和, g ( i , j ) g(i,j) g(i,j) 表示把区间删成连续上升一段的最大价值和, h ( i , j ) h(i,j) h(i,j) 表示把区间删成连续下降一段的最大价值和,那么转移方程可以是 f ( i , j ) = m a x { f ( i , k ) + f ( k + 1 , j ) , g ( i , k ) + h ( k , j ) + v 2 ⋅ w k − w i − w j + 1 } f(i,j)=max\{f(i,k)+f(k+1,j),g(i,k)+h(k,j)+v_{2·w_k-w_i-w_j+1}\} f(i,j)=max{f(i,k)+f(k+1,j),g(i,k)+h(k,j)+v2wkwiwj+1} ,后者也很容易理解,我考虑了锯齿一段(这里只考虑上凸的锯齿,下凹的是一样的状态),显然当只有 g ( i , k ) g(i,k) g(i,k) 存在时候,左边区间的大小就是 w k − w i + 1 w_k-w_i+1 wkwi+1 ,右边区间的大小就是 w k − w j + 1 w_k-w_j+1 wkwj+1 ,当然区间有一个重叠的地方就是 k k k 位置,因此长度就是 2 ⋅ w k − w i − w j + 1 2·w_k-w_i-w_j+1 2wkwiwj+1 !最后答案就是 f f f 的最大子序列和!接下来就是枚举的细节问题了。还是挺常见的一种区间DP处理方法。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int f[404][404], g[404][404], h[404][404], w[404], v[404], a[404];
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    for (int i = 1; i <= n; i++) {
        f[i][i] = v[1];
        g[i][i] = h[i][i] = 0;
    }
    for (int l = 1; l <= n; l++) {
        for (int i = 1; i + l - 1 <= n; i++) {
            int j = i + l - 1;
            if (i == j) continue;
            f[i][j] = g[i][j] = h[i][j] = -1e8;
            for (int k = i + 1; k <= j; k++) {
                if (w[i] + 1 == w[k]) g[i][j] = max(g[i][j], f[i + 1][k - 1] + g[k][j]);
                if (w[i] - 1 == w[k]) h[i][j] = max(h[i][j], f[i + 1][k - 1] + h[k][j]);
                f[i][j] = max(f[i][j], f[i][k - 1] + f[k][j]);
            }
            for (int k = i; k <= j; k++) {
                ll len = 2LL * w[k] - w[i] - w[j] + 1;
                if (0 <= len && len <= n) f[i][j] = max(f[i][j], g[i][k] + h[k][j] + v[len]);
            }
        }
    }
    a[0] = 0;
    for (int j = 1; j <= n; j++) {
        a[j] = a[j - 1];
        for (int i = 1; i <= j; i++) a[j] = max(a[j], a[i - 1] + f[i][j]);
    }
    printf("%d\n", a[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值