『最长等差数列 线性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; 
} 

考点:灵活的状态设置。


<后记>

猜你喜欢

转载自www.cnblogs.com/Parsnip/p/10210595.html