算法学习之寻找最长等差数列

  最长等差数列就是在一个数组中,组成等差数列的最长的那一个,首先我们对数组排序,然后我们一般会先想到暴力法从第一个开始循环遍历整个数组,时间复杂度O(N^3),下面给出伪代码

               int i,j,k,len = 0
               for(i = 0; i < n; i++)
                     for(j = i+1; j < n; j++)
                    {
                        int delta = a[j] - a[i];
                        len = 2;
                        int tmp = a[j];
                        for(k = j+1; k < n; k++)
                       {
                            if(a[k] - tmp== delta)
                          {
                               len++;
                               tmp = a[k];
                          }
                       }
                      求出最大的len
                    }


第二种方法用动态规划,我们可以利用等差数列的性质来做,当等差数列长度为奇数的时候,比如相邻3个数,aiajak

则2*aj = ai+ak;只要任何一个关于它对称的两个数相加都等于它的2倍,有了这个性质我们就可以利用动态规划来做

dp[i][j] = d,表示以a[i]a[j]为前两个元素的等差数列长度为d,我们再来推状态转移方程,

 如果ai+ak<2*aj,说明ak - aj < aj - ai dp[i][j]的值不能确定,k接着往后走

如果ai+ak>2*aj,说明ak-aj > aj -ai,此时dp[i][j]的值为2,因为k不能往后走了,再往后走二者差距更大,所以a[i]a[j]开头的等差数列为2,然后再i--,开始寻找下个ai aj开头的等差数列

如果ai+ak==2*aj,则说明这三个数构成等差数列,此时dp[i][j]=dp[j][k]+1;也就是说以ajak开头的等差数列前加上了一个ai,状态转移确定好后,下面开始写代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct result
{
    int max_len;
    int i;
    int j;
};
int max(int a,int b)
{
    return a > b ? a : b;
}
void find_llap(int *arry,int n)
{
    int dp[50][50]; //dp[i][j]代表以arry[i]和arry[j]为头两个元素的等差数列,此题规模小,规模大的时候i,j可以做hash,就可以节省空间
    int i,j,k;
    struct result result;
    memset(dp,0,sizeof(dp));
    for(i = 0; i < n; i++)
    {
       dp[i][n] = 2; //因为以arry[n]为第二个元素的等差数列长度一定为2
    }
    if(n < 2)
    {
        for(i = 0; i < 2; i++)
            printf("%d ",arry[i]);
        return;
    }
    result.max_len = 0;
    for(j = n-1; j >= 1; j--)
    {
        i = j-1;k = j+1;
        while(i >= 0 && k <= n)
        {
            if(arry[i]+arry[k] > arry[j]<<1) //如果和ai+ak>2aj,说明ak-aj>aj-ai,这个构造不成等差数列,则k以后的更不行,因为数组是升序
             {
                 dp[i][j] = 2;                //所以此步骤直接赋值为2,及只有ai aj构造成等差数列
                 i--;
             }
            else if(arry[i]+arry[k] < arry[j]<<1)
            {
                k++;       //如果ai+ak<2aj,说明ak-aj<aj-ai,所以以ai aj开头的等差数列,往后遍历,可能还有新的元素符合公差,所以k++
            }
            else
            {
                /*如果ai+ak=2aj,则正好构成等差数列,直接将前一个子问题的解+1即可*/
                dp[i][j] = dp[j][k]+1;
                result.max_len = max(result.max_len,dp[i][j]);
                if(result.max_len == dp[i][j])
                {
                    result.i = i;
                    result.j = j;
                }
                i--;k++;
            }
         }
         if(result.max_len == 0)
         {
             result.max_len = 2;
             result.i = 0;
             result.j = 1;
         }
         while(i >= 0) /*这个循环是因为k已经到最后了,所以以这个j为第二个元素的所有数列都是2*/
         {
             dp[i][j] = 2;
             i--;
         }
     }
     int delta = arry[result.j] - arry[result.i];
     int last  = arry[result.i]+(result.max_len-1)*delta;
     for(i = arry[result.i]; i <= last; i+=delta)
        printf("%d ",i);

}

int main()
{
    int n;
    int arry[100];
    scanf("%d",&n);
    int i;
    for(i = 0; i < n; i++)
      scanf("%d",&arry[i]);
    find_llap(arry,n-1);
    return 0;
}


  依据这个性质,还有一种动态规划,dp[i][j]代表等差数列最后两个元素,然后从前往后遍历,得到的结果一样

      


              

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值