problem
定义长度为 nnn 的“好”的串 aaa 满足:
- ∣ai−ai−1∣=1,i∈[2,n]|a_i-a_{i-1}|=1,i\in[2,n]∣ai−ai−1∣=1,i∈[2,n]。
- ai≥ai−1+ai+12,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]。
给定长度为 nnn 的序列 a,va,va,v,每次选择一段 aaa 的“好”子串删除,剩下的子串拼接在一起。
若删除子串长度为 xxx,则会获得 vxv_xvx 的价值。
不一定要删完,求最大价值。
n≤400,∣vi∣≤1e5,1≤ai≤1e9n\le 400,|v_i|\le 1e5,1\le a_i\le 1e9n≤400,∣vi∣≤1e5,1≤ai≤1e9。
solution
observation
:子串要么是单增,要么是单减,要么是先单增后单减,即/
,\
,/\
。显然 \/
情况下的最低点无法满足条件 222。
先不考虑“不一定全删完”,我们就要求必须删完。
设 fl,r:[l,r]f_{l,r}:[l,r]fl,r:[l,r] 全部删完的最大价值。显然是区间 dpdpdp 转移。(dpdpdp 转移就是要归到子问题上)
-
考虑最后一次删除 l,rl,rl,r 不是一起被删的,那么肯定存在一个中间点 ppp 分割这段区间。
即 fl,r←fl,p+fp+1,rf_{l,r}\leftarrow f_{l,p}+f_{p+1,r}fl,r←fl,p+fp+1,r。
-
考虑最后一次删除 l,rl,rl,r 是一起被删的。
那么我们就需要知道最后一次的单增/单减序列的价值。
设 gl,r:[l,r]g_{l,r}:[l,r]gl,r:[l,r] 删到只剩一个单增序列的最大价值。
设 hl,r:[l,r]h_{l,r}:[l,r]hl,r:[l,r] 删到只剩一个单减序列的最大价值。
我们直接枚举两个序列的交点,即最高点 iii。
fl,r←gl,i+hi,r+v(ai∗2−al−ar+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)。
当然还有只有单增/单减情况,但要求 al,ara_l,a_ral,ar 满足相应大小关系。
fl,r←gl,r+v(ar−al+1)al≤arf_{l,r}\leftarrow g_{l,r}+v(a_r-a_l+1)\quad a_l\le a_rfl,r←gl,r+v(ar−al+1)al≤ar。
fl,r←hl,r+v(al−ar+1)al≥arf_{l,r}\leftarrow h_{l,r}+v(a_l-a_r+1)\quad a_l\ge a_rfl,r←hl,r+v(al−ar+1)al≥ar。
至于 g,hg,hg,h 的转移,同样枚举接在 rrr 前的元素 iii,即可变成子问题。
gl,r←gl,i+fi+1,r−1ai+1=arg_{l,r}\leftarrow g_{l,i}+f_{i+1,r-1}\quad a_i+1=a_rgl,r←gl,i+fi+1,r−1ai+1=ar。
hl,r←hl,i+fi+1,r−1ai−1=arh_{l,r}\leftarrow h_{l,i}+f_{i+1,r-1}\quad a_i-1=a_rhl,r←hl,i+fi+1,r−1ai−1=ar。
最后再来个 dpi:[1,i]dp_i:[1,i]dpi:[1,i] 一定删掉 [k,i][k,i][k,i] 连续一段,但前面不要求删完的最大价值。
枚举 jjj,dpj+fk,i→dpidp_j+f_{k,i}\rightarrow dp_idpj+fk,i→dpi,前缀最大值优化,转移,dpndp_ndpn 就是最后的结果了。
时间复杂度 O(n3)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;
}