zoukankan      html  css  js  c++  java
  • 连续子数组最大和

    1. 问题描述

    输入一个整形数组,求数组中连续的子数组使其和最大。比如,数组x

    应该返回 x[2..6]的和187.

    2. 问题解决

    我们很自然地能想到穷举的办法,穷举所有的子数组的之和,找出最大值。

    穷举法

    i, j的for循环表示x[i..j],k的for循环用来计算x[i..j]之和。

    maxsofar = 0
    for i = [0, n)
        for j = [i, n)
            sum = 0
            for k = [i, j]
                sum += x[k]
            /* sum is sum of x[i..j] */
            maxsofar = max(maxsofar, sum)
    

    有三层循环,穷举法的时间复杂度为(O(n^3))

    对穷举法的改进1

    我们注意到x[i..j]之和 = x[i..j-1]之和 + x[j],因此在j的for循环中,可直接求出sum。

    maxsofar = 0
    for i = [0, n)
        sum = 0
        for j = [i, n)
            sum += x[j]
            /* sum is sum of x[i..j] */
            maxsofar = max(maxsofar, sum)
    

    显然,改进之后的时间复杂度变为(O(n^2))

    对穷举法的改进2

    在计算fibonacci数时,应该还有印象:用一个累加数组(cumulative array)记录前面n-1次之和,计算当前时只需加上n即可。同样地,我们用累加数组cumarr记录:cumarr[i] = x[0] + . . . +x[i],那么x [i.. j]之和 = cumarr[j] -cumarr[i - 1]

    cumarr[-1] = 0
    for i = [0, n)
        cumarr[i] = cumarr[i-1] + x[i]
        
    maxsofar = 0
    for i = [0, n)
        for j = [i, n)
            sum = cumarr[j] - cumarr[i-1]
            /* sum is sum of x[i..j] */
            maxsofar = max(maxsofar, sum)
    

    时间复杂度依然为(O(n^2))

    分治法

    所谓分治法,是指将一个问题分解为两个子问题,然后分而解决之。具体步骤如下:

    • 先将数组分为两个等长的子数组a, b;

    • 分别求出两个数组a,b的连续子数组之和;

    • 还有一种情况(容易忽略):有可能最大和的子数组跨越两个数组;

    • 最后比较(m_a), (m_b), (m_c),取最大即可。

    在计算(m_c)时,注意:(m_c)必定包含总区间的中间元素,因此求(m_c)等价于从中间元素开始往左累加的最大值 + 从中间元素开始往右累加的最大值

    float maxsum3(l, u)
        if (l > u) /* zero elements */
            return 0
            
        if (l == u) /* one element */
            return max(0, x[l])
        
        m = (l + u) / 2
        /* find max crossing to left */
        lmax = sum = 0
        for (i = m; i >= l; i--)
            sum += x[i]
            lmax = max(lmax, sum)
        
        /* find max crossing to right */
        rmax = sum = 0
        for i = (m, u]
            sum += x[i]
            rmax = max(rmax, sum)
    
        return max(lmax+rmax,
                    maxsum3(l, m),
                    maxsum3(m+1, u));
    

    容易证明,时间复杂度为(O(n*log n))

    动态规划

    Kadane算法又被称为扫描法,为动态规划(dynamic programming)的一个典型应用。我们用DP来解决最大子数组和问题:对于数组(a),用(c_i)标记子数组(a[0..i])的最大和,那么则有

    [c_i = max { a_i, c_{i-1} + a_i } ]

    子数组最大和即为(max c_i)。Kadane算法比上面DP更进一步,不需要用一个数组来记录中间子数组和。通过观察容易得到:若(c_{i-1} leq 0),则(c_i = a_i)。用(e)表示以当前为结束的子数组的最大和,以替代数组(c);那么

    [e = max { a_i, e + a_i } ]

    Python实现如下:

    def max_subarray(A):
        max_ending_here = max_so_far = A[0]
        for x in A[1:]:
            max_ending_here = max(x, max_ending_here + x)
            max_so_far = max(max_so_far, max_ending_here)
        return max_so_far
    

    max_ending_here对应于标记(e)max_so_far记录已扫描到的子数组的最大和。Kadane算法只扫描了一遍数组,因此时间复杂度为(O(n)).

    3. 参考资料

    [1] Jon Bentley, Programming Pearls.
    [2] GeeksforGeeks, Largest Sum Contiguous Subarray.

  • 相关阅读:
    python基础
    放假七天
    欠缺的
    烦人不能评论
    他妹的不能用客户端评论
    框架与食材
    270@365
    opencv4.2.0.34+python3.8.2+(直线检测、圆检测、轮廓发现、对象测量、膨胀和腐蚀、开闭操作、形态学操作、分水岭算法、人脸检测、识别验证码)
    opencv4.2.0.34+python3.8.2+(图像直方图、直方图反向投影、模板匹配、图像二值化、超大图像二值化、高斯金字塔和拉普拉斯金字塔 、图像梯度)
    opencv4.2.0.34+python3.8.2+(获取图片视频并打开、numpy、色彩空间、数值与逻辑计算、图像的切割合并填充、floodFill、卷积模糊处理、高斯噪声处理高斯模糊、ERF)
  • 原文地址:https://www.cnblogs.com/en-heng/p/3970231.html
Copyright © 2011-2022 走看看