• 【转】编程之美2.14——求数组的子数组之和的最大值


    问题:
    1. 一个由N个整数元素的一维数组,求其所有子数组中元素和的最大值。
    2. 如果数组首尾相邻,也就是允许子数组A[i],...,A[n-1],A[0],...,A[j]存在,求其所有子数组总元素和的最大值。

    1. 解法:
    我们使用动态规划的思想可以在O(n)的时间内计算出子数组之和最大值。
    动态规划问题往往非常灵活,所以在做题的时候不知道如何编写,其实它有很清晰的分析方法,按照这个方法就可以较容易的解决动态规划的问题。具体参考IOI2000 张辰的论文《动态规划的特点及其应用》
    阶段:在所有以元素k结尾的子数组中,选出元素和最大的子数组,k=1,2...n。
    状态:以元素k结尾的和最大的子数组是包含以元素k-1结尾的和最大的子数组还是就只有元素k这一个元素,即有这两个可选状态。
    之所以选择这样的阶段和状态,因为这种选择方式具有无后效性,即阶段k+1(以元素k+1结尾的子数组)只会与阶段k(以元素k结尾的子数组)发生关联,而与其它阶段无关。在得到以每个元素结尾的和最大的子数组之后,只要取其中最大值就是所有子数组中最大的子数组。

    #include <iostream> 
    #include <algorithm> 
    using namespace std; 
     
    #define MAXN 1003 
    int A[MAXN]; 
     
    // 动态规划思想,时间复杂度O(n) 
    int Tail[MAXN]; 
     
    int main() 
    { 
        int n, i, j, k; 
        cin >> n; 
        for (i=1; i<=n; i++) 
            cin >> A[i]; 
        // 计算以k结尾的子数组之和的最大值,即子数组包含第k个数 
        Tail[1] = A[1]; 
        for (k=2; k<=n; k++) // k个阶段 
            Tail[k] = max(A[k],Tail[k-1]+A[k]); // 只有两个状态 
        // 因为和最大的子数组肯定以某个数结尾,所以取这n个子数组的最大值 
        // 就是和最大的子数组 
        int All = Tail[1]; 
        for (i=2; i<=n; i++) 
            All = max(All, Tail[i]); 
        cout << All; 
    } 

    虽然这种标准的动态规划方法时间复杂度已经最优了,但它仍要占用O(n)的空间,对于一般的动态规划问题占用较多的空间是不可避免的,但这个问题较简单,仍可以继续优化。我们把All取最大值的操作放入Tail的计算循环中,如下:

    Tail[1] = A[1];
    All = Tail[1];
    for (k=2; k<=n; k++)// k个阶段
    {
    Tail[k] = max(A[k],Tail[k-1]+A[k]);// 只有两个状态
    All = max(All, Tail[k]);
    }
    由于循环体中只关心当前的Tail[k]和上一个Tail[k-1],可以省去之前所计算出的Tail[1],Tail[2]...Tail[k-2]的数据,如下:
    Tail = A[1];
    All = Tail;
    for (k=2; k<=n; k++)// k个阶段
    {
    Tail = max(A[k],Tail+A[k]);// 只有两个状态
    All = max(All, Tail);
    }

    最后优化后的代码就是书中最后给出的结果。

    2. 解法:
    把问题分为两种情况:
    (1)最大和子数组没有跨过A[n]到A[1](如问题1)
    (2)最大和子数组跨过A[n]到A[1]
    对于情况(2),这样的最大和子数组包含两个部分:以A[1]开始的最大和子数组,以及以A[n]结尾的最大和子数组,并且这两个子数组不允许重叠,那么将这两个子数组拼合起来就是情况(2)的解。

    #include <iostream> 
    #include <algorithm> 
    using namespace std; 
     
    #define MAXN 1003 
    int A[MAXN]; 
     
    int main() 
    { 
        int n, i, j, k; 
        cin >> n; 
        for (i=1; i<=n; i++) 
            cin >> A[i]; 
        // 和最大的子数组没有跨过A[n]和A[1] 
        int Tail = A[1]; 
        int All = Tail; 
        for (k=2; k<=n; k++) // k个阶段 
        { 
            Tail = max(A[k],Tail+A[k]); // 只有两个状态 
            All = max(All, Tail); 
        } 
        // 和最大的子数组跨过了A[n]和A[1] 
        int Start = A[1]; 
        for (i=2; i<=n && Start+A[i]>Start; i++) 
                Start += A[i]; 
        Tail = A[n]; 
        for (j=n-1; j>=1 && Tail+A[j]>Tail; j--) 
                Tail += A[j]; 
        if (i<j && Start+Tail > All) 
            All = Start+Tail; 
        cout << All; 
    } 

    转自http://www.2cto.com/kf/201207/139312.html

  • 相关阅读:
    OpenJudge百炼习题解答(C++)--题4010:2011
    Centos6.5卸载图形化
    nfs远程挂载问题记录
    走马观花-浪里跳-学习英文
    weblogic部署存在中文乱码导致部署失败
    KMS11激活Window系列
    mysql8.x开启远程登录
    notepad++插件实现json、xml格式化
    RHEL SHELL快捷键
    linux-env命令解析
  • 原文地址:https://www.cnblogs.com/budapeng/p/3437784.html
Copyright © 2020-2023  润新知