【日常刷题】最长等差数列

51nod1055·最长等差数列

题面

题目描述

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。

输入

第1行:N,N为正整数的数量(3 <= N <= 10000)。
第2 - N+1行:N个正整数。(2<= A[i] <= 10^9)

输出

最长等差数列的长度。

输入样例

10
1
3
5
6
8
9
10
12
13
14

输出样例

5


题解

显然是一道动态规划。

由于要等差数列,因此需要排序。

f [ i ] [ j ] f[i][j] f[i][j]为:第 i i i个数字作为等差数列的倒数第二位,第 j j j的数字作为等差数列最后一位的 最长等差数列。

必然,当 n ≥ 2 n≥2 n2时必然能够形成大小为 2 2 2的等差数列,故可得初始化 f [   ] [   ] = 2 f[\ ][\ ]=2 f[ ][ ]=2.我们需要通过动态规划来找出大于等于 3 3 3的长度的等差数列。

f [ i ] [ j ] = f [ k ] [ i ] + 1 f[i][j]=f[k][i]+1 f[i][j]=f[k][i]+1.表示以j结尾的等差数列添上第i位。这里保证 k < j < i k<j<i k<j<i a [ j ] + a [ k ] = 2 ∗ a [ i ] a[j]+a[k]=2*a[i] a[j]+a[k]=2a[i].
如果枚举每一个ijk,复杂度 O ( n 3 ) O(n^3) O(n3),妥妥的超时,所以我们可以优化枚举的状态。即枚举每一个i,根据第二个约束条件的大小关系逐渐将 k k k向左, j j j向右扫,并在达到平衡状态时进行转移。这样复杂度 O ( n 2 ) O(n^2) O(n2)

i的枚举方式,即顺序枚举保证了 f [ k ] [ i ] f[k][i] f[k][i]的存在,即中间数 ( k + i ) / 2 < i (k+i)/2<i (k+i)/2<i


C O D E CODE CODE

#include
using namespace std;
const int maxn=10005;
int n;
int a[maxn];
short int ans=2;
short int f[maxn][maxn];
int main(void)
{
	cin>>n;
	for (int i=1;i<=n;++i) cin>>a[i];
	for (int i=1;i<=n;++i)	
	    for (int j=i+1;j<=n;++j)
	        f[i][j]=2;
	sort(a+1,a+n+1);
	for (int i=2;i<n;++i)
	{
		int k=i-1,j=i+1;
		while (k>=1 && j<=n)
		{
		    if (a[k]+a[j]<2*a[i]) j++;
		    else if (a[k]+a[j]>2*a[i]) k--;
		    else f[i][j]=f[k][i]+1,ans=max(ans,f[i][j]),k--,j++;
		}
	} 
	cout<<ans<<endl;
	return 0;
} 

你可能感兴趣的:(动态规划DP)