这题由于删除了一个序列之后两端会合并,所以说如果DP的状态设置的不好的话会有后效性。
对于区间[l,r],如果r和之后的k个数连成了序列的话,那么[l,r-1]的元素就和后面要合并的k个元素就没有关系了。
用f[i][j][k]表示j和后面的k个数一起删除,i到j的区间删空能得到的最大价值。
f取出来的数在已知的部分i-j是
有峰的,发现直接用f转移有状态转移不全,再维护一个已知部分i-j递增没峰的。
然后考虑转移:
1、j和后面k个数直接合并。
2、j连到了[i,j-1]中的某个数,设这个数的位置为p,所以我们要考虑val[p]和val[j]的大小,发现有两种情况。val[p]>val[j]时,f[i][j][k]可以从g[i][p][k+1]+f[p+1][j-1][0]转移,表示p是峰或者从f[i][p][k+1]+f[p+1][j-1][0]转移,表示p之前还有峰。当val[p]<val[j]时,则必须限制j是当前的峰,所以用g转移。
3、g的转移和f类似,但只能从val[p]<val[j]转移过来,表示峰变高了。
4、由于这里的dp都是把题目消光的,于是我们再用一个n^2的dp,往优了取。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
const int N=160;
int x,n,cnt,p;
int ans[N];
int last[N],pre1[N],pre2[N],pre3[N],b[N],g[N][N][N],f[N][N][N],val[N];
std::map<int,int> hash;
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
std::cin>>n;
for(int i=1;i<=n;++i)
std::cin>>val[i];
for(int i=1;i<=n;++i)
{
std::cin>>b[i];
if(!hash[b[i]]) hash[b[i]]=++cnt;
}
for(int i=1;i<=n;++i)
{
pre1[i]=last[hash[b[i]-1]];
pre2[i]=last[hash[b[i]+1]];
pre3[i]=last[hash[b[i]]];
last[hash[b[i]]]=i;
}
memset(f,-127/3,sizeof(f));
memset(g,-127/3,sizeof(f));
for(int i=1;i<=n;++i)
{
for(int j=0;j<=n;++j)
if(i+j<=n) f[i][i][j]=g[i][i][j]=val[j+1];
else break;
f[i][i-1][0]=g[i][i-1][0]=0;
}
for(int l=1;l<=n;++l)
for(int i=1;i<=n;++i)
{
int j=i+l;
if(j>n) break;
for(int k=0;k<=n;++k)
{
if(j+k>n) break;
f[i][j][k]=std::max(f[i][j][k],f[i][j-1][0]+val[1+k]);
g[i][j][k]=std::max(g[i][j][k],f[i][j-1][0]+val[1+k]);
p=pre1[j];
while(p&&p>=i)
{
g[i][j][k]=std::max(g[i][p][k+1]+f[p+1][j-1][0],g[i][j][k]);
f[i][j][k]=std::max(g[i][p][k+1]+f[p+1][j-1][0],f[i][j][k]);
p=pre3[p];
}
p=pre2[j];
while(p&&p>=i)
{
f[i][j][k]=std::max(g[i][p][k+1]+f[p+1][j-1][0],f[i][j][k]);
f[i][j][k]=std::max(f[i][p][k+1]+f[p+1][j-1][0],f[i][j][k]);
p=pre3[p];
}
// printf("%d %d %d %d %d\n",i,j,k,g[i][j][k],f[i][j][k]);
}
}
for(int i=1;i<=n;++i)
{
ans[i]=ans[i-1];
for(int j=0;j<i;++j)
ans[i]=std::max(ans[i],ans[j]+f[j+1][i][0]);
}
printf("%d",ans[n]);
}