题意:
有一个公差集合{D},有n个数字
每次执行以下操作:
1. 在当前剩下的有序数组中选择x(x≥2)个连续数字;
2. 检查1选择的x个数字是否构成等差数列,且公差 d∈{D} ;
3. 如果2满足,可以在数组中删除这x个数字;
4. 重复 1−3 步,直到无法删除更多数字。
问最多能删掉多少个数字。
Solution:
一看就是区间dp,f[l][r]表示区间[l,r]中最多能删多少个数,因为所有的等差数列最终都可以拆成长度为2和3的数列,所以转移时我们只需要考虑长度为2和3的序列即可:预处理所有长度为2和3的区间,dp时分成两种情况:左右各成为等差序列,中间是等差序列,左右两端可以拼成等差序列
代码还是非常简洁的:
#include<cstdio>
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
int f[310][310];
int n,t,m,d[310];
map<int,bool>vis;
int a[310];
int main()
{
scanf("%d",&t);
while (t--)
{
memset(f,0,sizeof(f));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=m;i++) scanf("%d",&d[i]),vis[d[i]]=1;
for (int i=1;i<n;i++)
if (vis[a[i+1]-a[i]])f[i][i+1]=2;
for (int i=1;i<n-1;i++)
{
if (a[i]+a[i+2]==2*a[i+1]&&vis[a[i+1]-a[i]])f[i][i+2]=3;
else f[i][i+2]=max(f[i][i+1],f[i+1][i+2]);
}
for (int len=4;len<=n;len++)
for (int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
if (f[l+1][r-1]==len-2&&vis[a[r]-a[l]]) f[l][r]=len;
if (f[l+1][r-2]==len-3&&vis[a[r]-a[r-1]]&&a[l]+a[r]==2*a[r-1]) f[l][r]=len;
if (f[l+2][r-1]==len-3&&vis[a[l+1]-a[l]]&&a[l]+a[r]==2*a[l+1]) f[l][r]=len;
for (int k=l;k<r;k++)
f[l][r]=max(f[l][k]+f[k+1][r],f[l][r]);
}
printf("%d\n",f[1][n]);
for (int i=1;i<=m;i++) vis[d[i]]=0;
}
}