題目: LINK
這是BC上的題目,給你n個數a1, a2, ...., an. 這n個數的和爲m(m<=2^22),要在裏面取出儘可能多的數字(假設t個),組成新的序列b1, b2 ... bt.使得這個序列非遞減,而且(b(i) - b(i-1))也是非遞減的。求這個t的最大值。
題目沒有告訴n的大小。我們先排序統計出大小不同的數字nn[], 和每個數字出現的次數mm[],可以算出不同的數字不會超過3000
dp[i][j] (j>=i) 表示以nn[i] 和nn[j]爲所求序列的最末尾兩個數字,這種情況下的序列的長度.
dp[i][i] = mm[i], dp[i][j] = max(dp[i][j], dp[k][i] + 1), (k<=i)
直接算的話複雜度O(n^3),不可取。我們可以發現上面的轉移中隨着j的增大k是隨着減小的,可以優化到O(n^2).
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define INF 1000000000
//typedef __int64 LL;
#define N 1011111
#define M 3000
int t, n, m, num[N], nn[M],mm[M],dp[M][M];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i =1 ; i <= n; i ++) {
scanf("%d", &num[i]);
}
sort(num+1, num+1+n);
int tot = 0, cou = 1;
num[n+1] = -1;
for(int i = 2; i <= n+1; i++) {
if(num[i] != num[i-1]) {
tot ++;
nn[tot] = num[i-1];
mm[tot] = cou;
cou = 1;
}
else cou++;
}
int ans = 0;
for(int i = 1; i <= tot; i ++) {
dp[i][i] = mm[i];
ans = max(ans, dp[i][i]);
int k = i, ma = 0;
for(int j = i+1; j <= tot; j++) {
while(nn[j] - nn[i] >= nn[i] - nn[k] && k>=1) {
ma = max(ma, dp[k][i]);
k--;
}
dp[i][j] = ma+1;
ans = max(ans, dp[i][j]);
}
}
printf("%d\n", ans);
}
return 0;
}