CF392E Deleting Substrings
Solution
首先,题的意思是删连续上升的/连续下降的/先上升后的。
然后发现 \(n\leq 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\{[1\leq w_j-w_i+1\leq n](g_{i,k}+v_{w_j-w_i+1}),[1\leq w_i-w_j+1\leq 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}\{[1\leq 2w_k-w_i-w_j+1\leq 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\n",dp[n]);
return 0;
}