zoukankan      html  css  js  c++  java
  • 最大子段和

    N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
    例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。


    1.最大子段和问题的简单算法

    用数组a[]存储给定的n个整数a1,a2,……,an。

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define Max 50005
    int a[Max];
    int MaxSum(int n,int * a)
    {
        int sum = 0;
        for(int i = 0;i <= n;i ++)
        for(int j = i;j <= n;j ++)
        {
            int thissum = 0;
            for(int k = i;k <= j;k ++)
            thissum += a[k];
            if(thissum > sum)
            {
                sum = thissum;
                //besti = i;
                //bestj = j;
            }
        }
        return sum;
    }
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0;i < n;i ++)
        cin >> a[i];
        cout << MaxSum(n,a) << endl;
        return 0;
    }
    

    从这个算法的3个for循环可以看出,它所需要的计算时间是O(n^3)。事实上,如果注意到a[i]到a[j]的和= a[j]+ a[i]到a[j - 1]的和,则可将算法中的最后一个for循环省去,避免重复计算,从而使算法得以改进。改进后的代码为:

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define Max 50005
    int a[Max];
    int MaxSum(int n,int * a)
    {
        int sum = 0;
        for(int i = 0;i <= n;i ++)
        {
            int thissum = 0;
            for(int j = i;j <= n;j ++)
            {
                thissum += a[j];
                if(thissum > sum)
                sum = thissum;
            }
        }
        return sum;
    }
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0;i < n;i ++)
        cin >> a[i];
        cout << MaxSum(n,a) << endl;
        return 0;
    }

    改进后的算法显然只需要O(n^2)的计算时间。上述改进是在算法设计技巧上的一个改进,能充分利用已经得到的结果,避免重复计算,节省了计算时间。

    2.最大子段和问题的分治算法

          针对最大子段和这个具体问题本身的结构,还可以从算法设计的策略上对上述O(n^2)计算时间算法加以更深刻的改进。从这个问题的解的结构可以看出,它适合于用分治法求解。

            如果将所给的序列a[1:n]分为长度相等的两段a[1:n/2]和a[n/2 + 1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情形;

        (1)a[1:n]的最大子段和与a[1:n/2]的最大子段和相同;

        (2)a[1:n]的最大子段和与a[n/2 + 1:n]的最大子段和相同;

        (3)a[1:n]的最大子段和为a[i]到a[j]的和,且1 <= i <= n/2, n/2 + 1 <= j <= n。

    (1)和(2)这两种情形可递归求得。对于情形(3),容易看出,a[n/2]与a[n/2+1]在最优子序列中。因此,可以在a[1:n/2]中计算出s1 = 子段a[i]到a[n/2]的最大和,并在a[n/2+ 1:n]中计算出s2=子段a[n/2+1]到a[j]的最大和。则s1 + s2 即为出现情形(3)时的最优值。据此可设计出求最大子段和的分治算法如下:

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define Max 50005
    typedef long long LL;
    LL a[Max];
    LL MaxSubSum(LL *a,int left,int right)
    {
        LL sum = 0;
        if(left == right)
        sum = a[left] > 0 ? a[left] : 0;
        else
        {
            LL center = (left + right) / 2;
            LL leftsum = MaxSubSum(a,left,center);
            LL rightsum = MaxSubSum(a,center + 1,right);
            LL s1 = 0;
            LL lefts = 0;
            for(LL i = center;i >= left;i --)
            {
                lefts += a[i];
                if(lefts > s1)
                s1 = lefts;
            }
            LL s2 = 0;
            LL rights = 0;
            for(LL i = center + 1;i <= right;i ++)
            {
                rights += a[i];
                if(rights > s2)
                s2 = rights;
            }
            sum = s1 + s2;
            if(sum < leftsum)
            sum = leftsum;
            if(sum < rightsum)
            sum = rightsum;
        }
        return sum;
    }
    LL MaxSum(int n,LL * a)
    {
        return MaxSubSum(a,1,n);
    }
    int main()
    {
        int n;
        cin >> n;
        for(int i = 1;i <= n;i ++)
        cin >> a[i];
        cout << MaxSum(n,a) << endl;
        return 0;
    }

    该算法所需的计算时间T(n)=O(nlogn).

    3.最大子段和问题的动态规划算法

            在对上述分治算法的分析中注意到,若记b[j]=a[1:j]的最大子段和,则所求的最大子段和为

    a[1:n]的最大子段和=b[1 :n]的最大值

            由b[j]的定义易知,当b[j - 1]>0时b[j] = b[j - 1] + a[ j ],否则b[j] = a[j]。由此可得计算b[j]的动态规划递归式

                        b[ j ] = max{ b[ j - 1] + a[ j ],a[ j ]}, 1 <= j <= n

    据此,可设计出求最大子段和的动态规划算法如下。

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define Max 50005
    long long a[Max];
    long long MaxSum(int n,long long * a)
    {
        long long sum = 0,b = 0;
        for(int i = 1;i <= n;i ++)
        {
            if(b > 0)
            b += a[i];
            else
            b = a[i];
            if(b > sum)
            sum = b;
        }
        return sum;
    }
    int main()
    {
        int n;
        cin >> n;
        for(int i = 1;i <= n;i ++)
        cin >> a[i];
        cout << MaxSum(n,a) << endl;
        return 0;
    }

    上述算法显然需要O(n)计算时间和O(n)空间。

    例题:

    1049 最大子段和

    基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题
    N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
    例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
    Input
    第1行:整数序列的长度N(2 <= N <= 50000)
    第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
    Output
    输出最大子段和。
    Input示例
    6
    -2
    11
    -4
    13
    -5
    -2
    Output示例
    20
  • 相关阅读:
    flask读书笔记-flask web开发
    flask 杂记
    ubuntu redis 安装 &基本命令
    redis 订阅&发布(转载)
    Redis键值设计(转载)
    python2 python3区别
    捕获异常
    开源的微信个人号接口
    工具
    HDU2966 In case of failure(浅谈k-d tree)
  • 原文地址:https://www.cnblogs.com/lu1nacy/p/10016652.html
Copyright © 2011-2022 走看看