『最長等差數列 線性DP』
阿新 • • 發佈:2019-01-02
<更新提示>更新提示>
<第一次更新>第一次更新>
<正文>正文>
最長等差數列(51nod 1055)
Description
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 Format
第1行:N,N為正整數的數量(3 <= N <= 10000)。 第2 - N+1行:N個正整數。(2<= A[i] <= 10^9)
Output Format
最長等差數列的長度。
Sample Input
10
1
3
5
6
8
9
10
12
13
14
Sample Output
5
解析
對於一個序列的特定最優值求解,應該很容易想到是線性DP。第一步首先排序是很容易想到的。
第一個突破口在狀態的設定,如果直接用n設定狀態,發現會很難處理轉移的問題。
\(f[i][j]\)代表以\(a_i\)為第一項,\(a_j\)為第二項所構成的等差數列的最長長度\((i<j)\)
考慮若\(a_k\)可以作為這個等差數列的第三項,且滿足\((i<j<k)\),那麼\(f[i][j]=f[j][k]+1\)。
由等差數列的性質可以得知,當\(a_k\)
此時我們列舉\(j\),將\(i\),\(k\)設為指標利用性質的大小關係去掃描即可,可以做到時間複雜度\(O(n^2)\)。
當然,由於\((i<j<k)\),所以轉移時需要倒序列舉。
\(Code:\)
#include<bits/stdc++.h> using namespace std; inline void read(int &k) { int w=0,x=0;char ch; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); k=(w?-x:x);return; } const int N=10000+80; int n,a[N]; short int f[N][N]={},ans=2; inline void input(void) { read(n); for(int i=1;i<=n;i++)read(a[i]); } inline void init(void) { sort(a+1,a+n+1); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) f[i][j]=2; } inline void dp(void) { for(int j=n-1;j>=2;j--) { int i=j-1,k=j+1; while(i>=1&&k<=n) { if(a[j]*2==a[i]+a[k]) { f[i][j]=f[j][k]+1; ans=max(ans,f[i][j]); k++,i--; } if(a[j]*2>a[i]+a[k])k++; if(a[j]*2<a[i]+a[k])i--; } } } int main(void) { input(); init(); dp(); printf("%d\n",ans); return 0; }
考點:靈活的狀態設定。
<後記>後記>