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] ∣ai−ai−1∣=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] ai≥2ai−1+ai+1,i∈[2,n−1]。
给定长度为 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 n≤400,∣vi∣≤1e5,1≤ai≤1e9。
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,r←fl,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,r←gl,i+hi,r+v(ai∗2−al−ar+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,r←gl,r+v(ar−al+1)al≤ar。
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,r←hl,r+v(al−ar+1)al≥ar。
至于 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,r←gl,i+fi+1,r−1ai+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,r←hl,i+fi+1,r−1ai−1=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,i→dpi,前缀最大值优化,转移, 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;
}