对于一个长 的序列 ,你可以选择一个满足 且 的位置 并删除 和 ,删除后两边会拼接。
给定一个长 的序列 ,求最多能进行多少次上述操作。
,。
不会 *2600。
删除的过程不好刻画,考虑某个位置 和与它一起被删去的位置 (),显然 都要被删去,且它们一定先于 被删去。
这启发我们设计状态: 表示完全删除 至少需要在 中执行多少次删除操作。
转移考虑枚举和 一起删去的 。显然 或者 则 就无法被删去,否则设 。由于 和 一起删除,所以 前面最多能执行 次操作,故要求 ,此时有 。
边界为 。
求答案是简单的,设 表示 中最多执行多少次操作,利用 转移即可。
时间复杂度 ,代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=805,inf=1e8;
int n,a[S];
int f[S][S],g[S];
inline void slove()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<=n;i++) f[i+1][i]=0;
for(int len=2;len<=n;len+=2)
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
f[l][r]=inf;
if(l>=a[l]&&(l-a[l]&1^1))
{
int val=(l-a[l])/2;
for(int k=l+1;k<=r;k+=2)
if(f[l+1][k-1]<=val)
{
int pre=max(
val,
f[k+1][r]-(k-l+1)/2
);
f[l][r]=min(f[l][r],pre);
}
}
}
g[0]=0;
for(int i=1;i<=n;i++)
{
g[i]=g[i-1];
for(int j=i-1;j>=1;j-=2)
if(g[j-1]>=f[j][i]) g[i]=max(g[i],g[j-1]+(i-j+1)/2);
}
cout<<g[n]<<'\n';
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T;
cin>>T;
while(T-->0) slove();
return 0;
}