删好串(区间dp)

problem

定义长度为 n n n 的“好”的串 a a a 满足:

  • ∣ a i − a i − 1 ∣ = 1 , i ∈ [ 2 , n ] |a_i-a_{i-1}|=1,i\in[2,n] aiai1=1,i[2,n]
  • a i ≥ a i − 1 + a i + 1 2 , i ∈ [ 2 , n − 1 ] a_i\ge \frac{a_{i-1}+a_{i+1}}{2},i\in[2,n-1] ai2ai1+ai+1,i[2,n1]

给定长度为 n n n 的序列 a , v a,v a,v,每次选择一段 a a a 的“好”子串删除,剩下的子串拼接在一起。

若删除子串长度为 x x x,则会获得 v x v_x vx 的价值。

不一定要删完,求最大价值。

n ≤ 400 , ∣ v i ∣ ≤ 1 e 5 , 1 ≤ a i ≤ 1 e 9 n\le 400,|v_i|\le 1e5,1\le a_i\le 1e9 n400,vi1e5,1ai1e9

solution

observation:子串要么是单增,要么是单减,要么是先单增后单减,即/\/\。显然 \/ 情况下的最低点无法满足条件 2 2 2

先不考虑“不一定全删完”,我们就要求必须删完。

f l , r : [ l , r ] f_{l,r}:[l,r] fl,r:[l,r] 全部删完的最大价值。显然是区间 d p dp dp 转移。( d p dp dp 转移就是要归到子问题上)

  • 考虑最后一次删除 l , r l,r l,r 不是一起被删的,那么肯定存在一个中间点 p p p 分割这段区间。

    f l , r ← f l , p + f p + 1 , r f_{l,r}\leftarrow f_{l,p}+f_{p+1,r} fl,rfl,p+fp+1,r

  • 考虑最后一次删除 l , r l,r l,r 是一起被删的。

    那么我们就需要知道最后一次的单增/单减序列的价值。

    g l , r : [ l , r ] g_{l,r}:[l,r] gl,r:[l,r] 删到只剩一个单增序列的最大价值。

    h l , r : [ l , r ] h_{l,r}:[l,r] hl,r:[l,r] 删到只剩一个单减序列的最大价值。

    我们直接枚举两个序列的交点,即最高点 i i i

    f l , r ← g l , i + h i , r + v ( a i ∗ 2 − a l − a r + 1 ) f_{l,r}\leftarrow g_{l,i}+h_{i,r}+v(a_i*2-a_l-a_r+1) fl,rgl,i+hi,r+v(ai2alar+1)

    当然还有只有单增/单减情况,但要求 a l , a r a_l,a_r al,ar 满足相应大小关系。

    f l , r ← g l , r + v ( a r − a l + 1 ) a l ≤ a r f_{l,r}\leftarrow g_{l,r}+v(a_r-a_l+1)\quad a_l\le a_r fl,rgl,r+v(aral+1)alar

    f l , r ← h l , r + v ( a l − a r + 1 ) a l ≥ a r f_{l,r}\leftarrow h_{l,r}+v(a_l-a_r+1)\quad a_l\ge a_r fl,rhl,r+v(alar+1)alar

    至于 g , h g,h g,h 的转移,同样枚举接在 r r r 前的元素 i i i,即可变成子问题。

    g l , r ← g l , i + f i + 1 , r − 1 a i + 1 = a r g_{l,r}\leftarrow g_{l,i}+f_{i+1,r-1}\quad a_i+1=a_r gl,rgl,i+fi+1,r1ai+1=ar

    h l , r ← h l , i + f i + 1 , r − 1 a i − 1 = a r h_{l,r}\leftarrow h_{l,i}+f_{i+1,r-1}\quad a_i-1=a_r hl,rhl,i+fi+1,r1ai1=ar

最后再来个 d p i : [ 1 , i ] dp_i:[1,i] dpi:[1,i] 一定删掉 [ k , i ] [k,i] [k,i] 连续一段,但前面不要求删完的最大价值。

枚举 j j j d p j + f k , i → d p i dp_j+f_{k,i}\rightarrow dp_i dpj+fk,idpi,前缀最大值优化,转移, d p n dp_n dpn 就是最后的结果了。

时间复杂度 O ( n 3 ) O(n^3) O(n3)

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 405
#define int long long
#define inf 0x3f3f3f3f
int n;
int v[maxn], a[maxn], dp[maxn];
int f[maxn][maxn], g[maxn][maxn], h[maxn][maxn];

signed main() {
    freopen( "good.in", "r", stdin );
    freopen( "good.out", "w", stdout );
    scanf( "%lld", &n );
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &v[i] );
    for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
    //f[l][r]:删完[l,r]的最大价值
    for( int l = n;l;l -- )
        for( int r = l;r <= n;r ++ ) {
            f[l][r] = g[l][r] = h[l][r] = -inf;
            g[l][l] = h[l][l] = 0;
            for( int i = l;i <= r;i ++ ) {
                f[l][r] = max( f[l][r], f[l][i] + f[i + 1][r] );
                //case1 枚举断点
                if( a[i] - 1 == a[r] ) h[l][r] = max( h[l][r], h[l][i] + f[i + 1][r - 1] );
                //h[l][r]:将[l,r]删到只剩一个下坡的最大价值 且l,r一定不被删
                if( a[i] + 1 == a[r] ) g[l][r] = max( g[l][r], g[l][i] + f[i + 1][r - 1] );
                //g[l][r]:将[l,r]删到只剩一个上坡的最大价值 且l,r一定不被删
                if( a[l] <= a[i] and a[i] >= a[r] ) f[l][r] = max( f[l][r], g[l][i] + h[i][r] + v[(a[i] << 1) - a[l] - a[r] + 1] );
                //case2 枚举最高点/连接点
            }
            if( a[l] <= a[r] ) f[l][r] = max( f[l][r], g[l][r] + v[a[r] - a[l] + 1] );//case3直接一个递增序列无凸点
            if( a[l] >= a[r] ) f[l][r] = max( f[l][r], h[l][r] + v[a[l] - a[r] + 1] );//case4直接一个递减序列无凸点
        }
    for( int i = 1;i <= n;i ++ ) {
        dp[i] = max( dp[i], dp[i - 1] );
        for( int j = i;j <= n;j ++ ) dp[j] = max( dp[j], dp[i - 1] + f[i][j] );
        //枚举删去[l,r]一段并加上前面剩下的最大值(利用前缀最大值优化dp)
    }
    printf( "%lld\n", dp[n] );
    return 0;   
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值