先排序,再去重。去重之后数据量小于3000
然后用dp[i][k]表示以i和k两个节点作为结尾的能形成的符合题意的串的长度。
之后更新的时候,由于dp[i][k+1]肯定大于等于dp[i][k],因此向前找的时候,dp[i][k]停止的位置可以作为dp[i][k+1]开始寻找的位置,持续向前找。
注意细节,附代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int dp[3005][3005];
int v[(1<<22)+10];
int s[3005];
int n,m;
int cnt;
int ans;
int max(int a,int b)
{
return a>b?a:b;
}
void solve()
{
ans=0;
for (int i=1;i<=m;i++)
{
ans=max(ans,s[i]);
for (int k=i+1;k<=m;k++)
dp[i][k]=s[i]+1;
}
for (int i=1;i<=m;i++) //枚举i
{
int j=i-1; //j代表dp[j][i]中的j
int k=i+1;
while (k<=m) //枚举k
{
if (j!=0&&v[k]-v[i]>=v[i]-v[j]) //j==0,说明已经找到头,说明v[k]-v[i]>=v[i]-v[1],那么便不必再往前找了
{
dp[i][k]=max(dp[i][k],dp[j][i]+1);
j--;
}
else
{
dp[i][k+1]=dp[i][k];
ans=(dp[i][k],ans); //记录答案
k++;
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&v[i]);
if (n==1)
{
printf("1\n");
continue;
}
sort(v+1,v+n+1);
cnt=0;
int pre=1;
s[0]=1;
ans=0;
for (int i=2;i<=n;i++)
{
if (v[i]!=v[i-1])
{
cnt++;
s[cnt]=i-pre;
pre=i;
}
}
s[++cnt]=n-pre+1;
m=unique(v,v+n+1)-(v+1);
if (m==1) //想省点时间,结果发现没用
{
printf("%d\n",s[1]);
continue ;
}
solve();
printf("%d\n",ans);
}
}