vjudge传送门

题意:

有一个公差集合{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;
    }
}