题意:给一串数a[],这一串数的和是确定的,为M(M<=4194304)
从a[]数组中选t个数重新组成新的数组c[],使得任意c[i+1]-c[i]>=c[i]-c[i-1];
且对于每个a[k]不能重复选择(1<=k<=n)。
题解:
首先考虑解的结构一定是
C1,C1,…,C1,C2,C3,…,Cm
这种形式,其中满足
C1<C2<C3<…<Cm
所以对
a1,a2,a3,…,an
去重后从小到大排序得到
c1,c2,c3,…,cx
其中x是sqrt(M)级别的,用DP[i][j]表示以
ci
和
cj
结尾的满足条件的最长序列
首先初值化
DP[i][i]=count(ci)
即
ci
在原序列中的个数。
而
dp[i][j]=max(dp[k][i]
其中
k≤i
还满足
ci−ck≤cj−ci)+1
这样的复杂度是 O(x^3),在题中x最大为1000级别所以会超时,要使用下面优化
因为
dp[i][j]=max(dp[k][i]
其中
k≤i
还满足
ci−ck≤cj−ci)+1
dp[i][j+1]=max(dp[k][i]
其中
k≤i
还满足
ci−ck≤cj+1−ci)+1
注意到
cj+1>cj
所以满足
ci−ck≤cj−ci
的dp[k][i]必然满足
ci−ck≤cj+1−ci
因而不必重复计算
即最后复杂度可以为O(x^2).
代码:
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> #define M 3000 #include<map> using namespace std; typedef __int64 LL; int dp[M+5][M+5]; // 以a[i],a[j]结尾的数列 的最大长度 int a[M*M+5]; map<int ,int >mymap; int main() { int T,n,m; int cnt,ans; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); mymap.clear(); for(int i=0;i<n;i++) { scanf("%d",a+i); mymap[a[i]]++; } sort(a,a+n); n=unique(a,a+n)-a; //去重 ans=1; for(int i=0;i<n;i++) { int k=i; // ① dp[i][i]=mymap[a[i]]; cnt=dp[i][i]; ans=max(cnt,ans); for(int j=i+1;j<n;j++) { for(;k>=0 && a[i]-a[k]<=a[j]-a[i];k--) //② ①②③实现了红字的优化 cnt=max(cnt,dp[k][i]+1); dp[i][j]=cnt; // ③ ans=max(cnt,ans); } // printf("i=%d ans=%d\n",i,ans); } printf("%d\n",ans); } return 0; }