Solution
首先,题的意思是删连续上升的/连续下降的/先上升后的。
然后发现 (nleq 400) ,所以可以考虑区间DP。
设 (f_{i,j}) 为删完 (w_i,cdots, w_j) 的最大分数, (g_{i,j}) 为将 (w_i,cdots,w_j) 删成以 (w_i) 开头, (w_j) 结尾的连续上升的最大分数, (h_{i,j}) 为将 (w_i,cdots,w_j) 删成以 (w_i) 开头, (w_j) 结尾的连续下降的最大分数。
可以列出状态转移方程:
[g_{i,j}=max{[w_{j-1}+1=w_j]g_{i,j-1},max_{k=i}^{j-2}{[w_k+1=w_j](g_{i,k}+f_{k+1,j-1})}}
]
意思是可以直接填 (w_j) ,也可以删除 (w_{k+1},cdots,w_{j-1}) ,然后填 (w_j) 。
[h_{i,j}=max{[w_{j-1}-1=w_j]h_{i,j-1},max_{k=i}^{j-2}{[w_k-1=w_j](h_{i,k}+f_{k+1,j-1})}}
]
和上面那个同理。
[f_{i,j}=max{[1leq w_j-w_i+1leq n](g_{i,k}+v_{w_j-w_i+1}),[1leq w_i-w_j+1leq n](h_{i,k}+v_{w_i-w_j+1}),\max_{k=i}^{j-1}{f_{i,k}+f_{k+1,j}},max_{k=i+1}^{j-1}{[1leq 2w_k-w_i-w_j+1leq n]g_{i,k}+h_{k,j}+v_{2w_k-w_i-w_j+1}}}
]
最外层 (max) 中,第一个是删成连续上升的,第二个是删成连续下降的,第三个是先删 (w_i,cdots,w_k) 即 (f_{i,k}) 再删 (w_k,cdots,w_j) 即 (f_{k,j}) ,第四个是删成先上升再下降的。
[dp_i=max_{j=0}^{j<i}{dp_{i-1},dp_j+f_{j+1,i}}
]
这是最显然的,就不解释了。
注意:要记得把 (f_{i,i}) 初始化为 (1) , (g_{i,i},h_{i,i}) 初始化为 (0)
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=410,INF=1e9;
int n;
int v[N],w[N],f[N][N],g[N][N],h[N][N],dp[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline void Max(int &a,int b){
if(a<b) a=b;
}
int main(){
n=read();
for(int i=1;i<=n;i++) v[i]=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=g[i][j]=h[i][j]=-INF;
for(int i=n;i>0;i--){
f[i][i]=v[1];
g[i][i]=h[i][i]=0;
for(int j=i+1;j<=n;j++){
for(int k=i;k<j-1;k++)
if(w[k]+1==w[j]) Max(g[i][j],g[i][k]+f[k+1][j-1]);
if(w[j-1]+1==w[j]) Max(g[i][j],g[i][j-1]);
}
for(int j=i+1;j<=n;j++){
for(int k=i;k<j-1;k++)
if(w[k]-1==w[j]) Max(h[i][j],h[i][k]+f[k+1][j-1]);
if(w[j-1]-1==w[j]) Max(h[i][j],h[i][j-1]);
}
for(int j=i;j<=n;j++){
if(w[j]-w[i]+1>0&&w[j]-w[i]+1<=n) Max(f[i][j],g[i][j]+v[w[j]-w[i]+1]);
if(w[i]-w[j]+1>0&&w[i]-w[j]+1<=n) Max(f[i][j],h[i][j]+v[w[i]-w[j]+1]);
for(int k=i;k<j;k++) Max(f[i][j],f[i][k]+f[k+1][j]);
for(int k=i+1;k<j;k++)
if(2*w[k]-w[i]-w[j]+1>0&&2*w[k]-w[i]-w[j]+1<=n)
Max(f[i][j],g[i][k]+h[k][j]+v[2*w[k]-w[j]-w[i]+1]);
}
}
for(int i=1;i<=n;i++){
dp[i]=dp[i-1];
for(int j=0;j<i;j++) Max(dp[i],dp[j]+f[j+1][i]);
}
printf("%d
",dp[n]);
return 0;
}