题目大意:
• 给定vi,wi,每次可以在wi中选择一个子段[l,r]满足:
• |wi-wi+1|=1 (l<=i<r)
• 2wi-wi-1-wi+1>=0 (l<i<r)
• 选择后获得vr-l+1的收益并把这个子段删除,可以不删完,求最大收益。
• n<=400
Examples
input
3
0 0 3
1 2 1
output
3
input
6
1 4 5 6 7 1000
2 1 1 2 2 3
output
12
题解:
我们删除的区间是一个单峰上凸的区间。
用f[i][j]表示将i到j全部删完得到的最大代价,g[i][j]表示将i到j的区间删成w[i],w[i]+1,……,w[j]的最大收益,h[i][j]表示将i到j的区间删成w[i],w[i]-1,……,w[j]的最大收益。
f[i][j]可以由f[i][k]+f[k+1][j]||g[i][k]+h[k][j]+v[2*w[k]-w[i]-w[j]+1]转移过来。
h[i][j]和g[i][j]怎么维护呢? 当w[k]==w[i+1]时g[i][j]<-f[i+1][k-1]+g[k][j],h[i][j]同理。
代码:
#include<cstdio> #include<algorithm> #define inf 1<<28 using namespace std; template<class T>inline void read(T &x) { x=0;int f=0;char ch=getchar(); while(ch<'0'||ch>'9')f|=(ch=='-'),ch=getchar(); while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); x=f?-x:x; return; } int n,v[410],w[410],f[410][410],h[410][410],g[410][410],ans[410]; int main() { read(n); for(int i=1;i<=n;i++)read(v[i]); for(int i=1;i<=n;i++)read(w[i]); for(int l=1;l<=n;l++) { for(int i=1;i+l-1<=n;i++) { int j=i+l-1; if(i==j){g[i][j]=h[i][j]=0;f[i][j]=v[1];continue;} g[i][j]=h[i][j]=-inf; for(int k=i+1;k<=j;k++) if(w[k]==w[i]+1)g[i][j]=max(g[i][j],f[i+1][k-1]+g[k][j]); else if(w[k]==w[i]-1)h[i][j]=max(h[i][j],f[i+1][k-1]+h[k][j]); f[i][j]=-inf; for(int k=i;k<j;k++) f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]); for(int k=i;k<=j;k++) if(g[i][k]>-1<<20&&h[k][j]>-1<<20)f[i][j]=max(f[i][j],g[i][k]+h[k][j]+v[2*w[k]-w[i]-w[j]+1]); } } for(int i=1;i<=n;i++) { ans[i]=ans[i-1]; for(int j=1;j<=i;j++) ans[i]=max(ans[i],ans[j-1]+f[j][i]); } printf("%d ",ans[n]); return 0; }