连续最大子序列的几种解法

所谓最大连续子序列,就是求出给定一串数字中连续的那一段元素和的最大值。

举例如:

给定序列:2 3 -1 4 7  则最大子序列的值应为4+7=11.

 

先说第一种方法:暴力枚举数组中的元素,不断求和,进行更新即可。可是时间复杂度上一般承受不了。O(n^3)的时间复杂度确实对一般的问题很难接受。

 

第二种:把a[0]初始化为max值,则枚举n个位置,每次把sum置为0,然后从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。

部分代码如下:

inline int maxsequence(int arr[], int len)
{
    int max = arr[0]; //初始化最大值为第一个元素
    for (int i=0; i<len; i++) {
        int sum = 0; //sum必须清零
        for (int j=i; j<len; j++) { //从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。
            sum += arr[j];
            if (sum > max)
                max = sum;
        }
    }
    return max;
}


 

 

第三种:分治法。原序列可以分为两部分,不断递归计算出这两块序列的和,然后从中间向两端遍历,找到最大的序列的和。

部分代码如下:

/*求三个数最大值*/
inline int max3(int i, int j, int k)
{
    if (i>=j && i>=k)
        return i;
    return max3(j, k, i);
}

inline int maxsequence2(int a[], int l, int u)
{
    if (l > u) return 0;
    if (l == u) return a[l];
    int m = (l + u) / 2;

    /*求横跨左右的最大连续子序列左半部分*/
    int lmax=a[m], lsum=0;
    for (int i=m; i>=l; i--)
    {
        lsum += a[i];
        if (lsum > lmax)
            lmax = lsum;
    }

    /*求横跨左右的最大连续子序列右半部分*/
    int rmax=a[m+1], rsum = 0;
    for (int i=m+1; i<=u; i++)
     {
            rsum += a[i];
            if (rsum > rmax)
            rmax = rsum;
    }
    return max3(lmax+rmax, maxsequence2(a, l, m), maxsequence2(a, m+1, u)); //返回三者最大值
}


 

第四种:读入的时候预处理出一个前缀和数组,即sum[i]表示a[o]+a[1]+a[2]+.....+a[i-1];

然后又是枚举了,但实际这个复杂度也偏高,效率不太理想。

 

第五种:DP,最简单的DP吧,这个不知道DP的人也可以很容易写出来,用dp[i]表示以a[i]结尾所获得的最大值,那么显然有:dp[i]=max(dp[i-1]+a[i],a[i]);不断更新这个dp数组即可,时间复杂度最优,O(n)。

 

另外值得一提的是,还有一种变形的最大连续子序列的问题,简单的如这个题的B题

最常见的也是稍微有点麻烦的就是求最大子矩阵的问题,以前一直不会算,最后稍微想下就明白了。给出的是二维数组,但是求的值相当于还是应用的最大连续子序列的思想。具体做法就是把矩阵转换为一维的来算。

也就是枚举矩阵的连续几行的合并,这样就转换为一维的了,再用最大子序列的算法去求,更新最大值就可以了

下面附上几种求连续最大子序列的代码:

 

 

#include <iostream>
#include <cstdio>
#include <cstring>

int dp[501][501];
using namespace std;

inline int maxsequence(int arr[], int len)
{
    int max = arr[0]; //初始化最大值为第一个元素
    for (int i=0; i<len; i++) {
        int sum = 0; //sum必须清零
        for (int j=i; j<len; j++) { //从位置i开始计算从i开始的最大连续子序列和的大小,如果大于max,则更新max。
            sum += arr[j];
            if (sum > max)
                max = sum;
        }
    }
    return max;
}

/*求三个数最大值*/
inline int max3(int i, int j, int k)
{
    if (i>=j && i>=k)
        return i;
    return max3(j, k, i);
}

inline int maxsequence2(int a[], int l, int u)
{
    if (l > u) return 0;
    if (l == u) return a[l];
    int m = (l + u) / 2;

    /*求横跨左右的最大连续子序列左半部分*/
    int lmax=a[m], lsum=0;
    for (int i=m; i>=l; i--)
    {
        lsum += a[i];
        if (lsum > lmax)
            lmax = lsum;
    }

    /*求横跨左右的最大连续子序列右半部分*/
    int rmax=a[m+1], rsum = 0;
    for (int i=m+1; i<=u; i++)
     {
            rsum += a[i];
            if (rsum > rmax)
            rmax = rsum;
    }
    return max3(lmax+rmax, maxsequence2(a, l, m), maxsequence2(a, m+1, u)); //返回三者最大值
}

int maxsequence3(int a[], int len)
{
    int maxsum, maxhere;
    maxsum = maxhere = a[0];   //初始化最大和为a【0】
    for (int i=1; i<len; i++) {
        if (maxhere <= 0)
            maxhere = a[i];  //如果前面位置最大连续子序列和小于等于0,则以当前位置i结尾的最大连续子序列和为a[i]
        else
            maxhere += a[i]; //如果前面位置最大连续子序列和大于0,则以当前位置i结尾的最大连续子序列和为它们两者之和
        if (maxhere > maxsum) {
            maxsum = maxhere;  //更新最大连续子序列和
        }
    }
    return maxsum;
}

int main()
{
    int a[100];
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        cout<<maxsequence3(a,n)<<endl;
    }
    return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值