算法重拾之路——最大子段和

***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************



第二章:动态规划



>最大子段和<




算法描述:

    ▪给定由n个整数(可能为负整数)组成的序列 a1,a2, ... , an ,求该序列形如  从ai 到 aj (i ≤ j)的子段和的最大值。当所有整数均为负整数时定义其最大值为0。根据这个定义,所求的最优值为:

max{ 0 ,ai到aj的和(1≤i≤j≤n)}

   ▪一个例子:

      当有序列( a1,a2,a3,a4,a5,a6 )= (-2,11,-4,13,-5,-2)时,最大子段和为,a2+a3+a4=20



算法分析:

    对于最大子段和问题,有多种求解算法。

> 首先来一个 最简单的算法

    先用一个数组a[ ]来存储给定的n个整数,

    类似于穷举法,把每种情况都求一遍,所以是三重循环,时间复杂度就是O(n^3)

    代码可看下面 算法程序 的1-1


    但是这也可以优化一下,因为  ai到aj的和 等于 aj 加上 ai到aj-1的和,所以这样就可以把最后一个循环省去,避免重复计算,从而使算法得以改进。

    改进以后,这个算法只需要O(n^2)的计算时间。

    具体代码可看下面 算法程序 的 1-2


> 然后就是用分治法解决

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

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

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

③ a[1:n] 最大子段和为 ai 到 aj 的和,且 1≤i≤n/2 ,n/2+1 ≤ j ≤ n

       ①和②两种情况可以递归求,重点来说一下 ③情况:

比较容易看出,a[n/2] 与 a[n/2+1] 在最优序列之中。因此我们可以在a[1:n/2]中计算出s1(s1等于ai到an/2的和,而 1≤i≤n/2 ),同理,可以在a[n/2+1:n]中计算出s2(ai到an的和,n/2+1≤i≤n)。

    则s1+s2即为 ③时的情景。

    具体代码,可看下面 算法程序 的2-1


    该算法所需的计算时间T(n)满足典型的分治算法递归式:

    T(n)   =  O(1)                           n≤c

              2T(n/2)+O(n)                   n>c

    此递归方程解得  T(n) = O(nlogn)


>最大子段和的动态规划算法

    我们另外再声明一个数组 b[ ] ,

    记 b[j] = 为 1~j 的 最大子段和的值   且  1≤j≤n  

    从 b[j] 的定义,可以发现,当 b[j-1]>0 时,b[j] = b[j-1] + a[j] ;

    当 b[j-1] < 0 时, b[j] = a[j] 。

    所以,b[j] 动态规划的递归式为:

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


    再进行进一步优化,省略b[ ] 数组,

    就像穷举法省略掉第三个循环一样,

    此算法需要 O(n) 计算时间 和 O(n) 的计算空间。


    具体代码,可见 算法程序 的 3-1




算法程序:


穷举法 1-1:

// 穷举法 O(n^3)
int MaxSum( int n , int* a , int& besti, int& bestj )
{
    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;
}


1-2 穷举法优化:

// 1-2 穷举法的优化   O(n^2)
int MaxSum( int n , int* a , int& besti , int& bestj )
{
    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;
                besti = i;
                bestj = j;
            }
        }
    }
    return sum;
}

2-1 分治法:

// 2-1 分治法
int MaxSubSum( int* a , int left , int right )
{
    int sum = 0;
    if( left == right )
        sum = a[left]>0?a[left]:0;
    else    {
        int center = (left+right)/2;
        int leftsum = MaxSubSum(a,left,center);
        int rightsum = MaxSubSum(a,center+1,right);

        int s1 = 0;
        int lefts = 0;
        for( int i = center ; i >= left ; --i ) {
            lefts += a[i];
            if( lefts > s1 )
                s1 = lefts;
        }

        int s2 = 0;
        int rights = 0;
        for( int 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;
}

int MaxSum( int n , int* a )
{
    return MaxSubSum(a,0,n-1);
}

3-1 动态规划:

// 3-1 动态规划
int MaxSum( int n , int* a )
{
    int sum = 0, b = 0;
    for( int i = 0 ; i < n ; ++i )  {
        if( b > 0 )
            b += a[i];
        else
            b = a[i];
        if( b > sum )
            sum = b;
    }
    return sum;
}





***************************************转载请注明出处:http://blog.csdn.net/lttree ********************************************

你可能感兴趣的:(最大子段和,算法重拾之路)