1. 程式人生 > >51nod1055 最長等差數列

51nod1055 最長等差數列

ext 線性 雙指針 == ray 復雜度 sin for 線性dp

基準時間限制:2 秒 空間限制:262144 KB 分值: 80 N個不同的正整數,找出由這些數組成的最長的等差數列。 例如:1 3 5 6 8 9 10 12 13 14 等差子數列包括(僅包括兩項的不列舉) 1 3 5 1 5 9 13 3 6 9 12 3 8 13 5 9 13 6 8 10 12 14 其中6 8 10 12 14最長,長度為5。 Input
第1行:N,N為正整數的數量(3 <= N <= 10000)。
第2 - N+1行:N個正整數。(2<= A[i] <= 10^9)
Output
最長等差數列的長度。
Input示例
10
1
3
5
6
8
9
10
12
13
14
Output示例
5

動態規劃 線性DP

註意到是“由這些數組成的”等差序列,也就是原數列沒有順序限制

將數列從小到大排序,設$ f[i][j] $為 “末尾兩項是$ a[j] $和$ a[i] $的等差數列”的最長長度。

這個狀態設計挺巧妙的,既記錄了位置信息,又記錄了公差。

然後從$1$到$n$枚舉$i$,再枚舉$ j $和$ k $,當滿足 $ a[j]-a[k] = a[i]-a[j] $時,可以由$ f[j][k] $轉移到$ f[i][j] $

這樣做是$ O(n^3) $的。

註意到數列已經排好序了,轉移條件是 $ a[k]+a[i] == a[j] + a[j] $,其中蘊藏著神秘又美麗的單調性

所以可以枚舉 j,用雙指針維護i和k,這樣復雜度就降到 $ O(n^2)$了

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 using namespace std;
 7 const int
mxn=10002; 8 int read(){ 9 int x=0,f=1;char ch=getchar(); 10 while(ch<0 || ch>9){if(ch==-)f=-1;ch=getchar();} 11 while(ch>=0 && ch<=9){x=x*10+ch-0;ch=getchar();} 12 return x*f; 13 } 14 int n; 15 int a[mxn]; 16 short f[mxn][mxn]; 17 int main(){ 18 int i,j; 19 n=read(); 20 for(i=1;i<=n;i++){ 21 a[i]=read(); 22 } 23 sort(a+1,a+n+1); 24 int ans=2; 25 for(i=1;i<n;i++){ 26 j=i-1; 27 for(int k=i+1;k<=n && j>0;k++){ 28 while(j && a[k]+a[j]>a[i]+a[i])j--; 29 if(j && a[k]+a[j]==a[i]+a[i]){ 30 f[k][i]=(f[i][j]==0)?3:(f[i][j]+1); 31 ans=max(ans,(int)f[k][i]); 32 } 33 } 34 } 35 printf("%d\n",ans); 36 return 0; 37 }

51nod1055 最長等差數列