对于一个长 的序列 ,你可以选择一个满足 且 的位置 并删除 和 ,删除后两边会拼接。
给定一个长 的序列 ,求最多能进行多少次上述操作。
,。
不会 *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;
}
