1. 程式人生 > >『最長等差數列 線性DP』

『最長等差數列 線性DP』

<更新提示>

<第一次更新>


<正文>

最長等差數列(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\)

可以作為第三項時:\(a_j*2=a_i+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; 
} 

考點:靈活的狀態設定。


<後記>