算法设计——最大子段和问题分析

最大子段和问题——分治法应用


问题描述:
给定由n个整数(存在负整数)组成的序列 a1,a2,a3,,an a 1 , a 2 , a 3 , … … , a n ,求序列形式如 jk=iak ∑ k = i j a k 的子段和的最大值。当整个序列所有整数均为负整数时,其最大子段和为0。依次定义,所求的最大值为

max{0,max1ijnk=ijak} m a x { 0 , max 1 ≤ i ≤ j ≤ n ∑ k = i j a k }

例如:当 (a1,a2,a3,a4,a5,a6)=(2,11,4,13,5,2) ( a 1 , a 2 , a 3 , a 4 , a 5 , a 6 ) = ( − 2 , 11 , − 4 , 13 , − 5 , − 2 ) 时,最大子段和为 4k=2ak ∑ k = 2 4 a k


最大子段和问题的分治策略如下:

  1. 分解。如果将给定的序列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]的最大字段和与 jk=iak ∑ k = i j a k ,且满足 1in/2,n/2+1jn 1 ≤ i ≤ n / 2 , n / 2 + 1 ≤ j ≤ n
  2. 求解。第一种情况与第二种情况可以利用递归进行求解。对于第三种情况,经过分析得出,A[n/2]与A[n/2+1]在最优子序列中。因此可以在A[1…n/2]中计算出 sum1=max1in/2n2k=iak s u m 1 = max 1 ≤ i ≤ n / 2 ∑ k = i n 2 a k ,并在A[n/2+1…n]中计算出 sum2=maxn/2inik=n/2ak s u m 2 = max n / 2 ≤ i ≤ n ∑ k = n / 2 i a k ,则最终计算出 sum=sum1+sum2 s u m = s u m 1 + s u m 2 为第三种情况下的最优解。

  3. 合并。比较再分解阶段的3种情况下的最大子段和,获得三者之中的最大值即为问题的求解。

根据以上分析我们可以设计出求解最大子段和的分治算法如下:

/*--------------最大子段和问题  分治法应用-----------------------------------
GetMaxSubSum()说明:
Array为数组、left和right为数组下标
    对于数组Array长度为n的序列,可以调用GetMaxSubSum(Array,0,n-1)来获得数组Array中的最大字段和
----------------------------------------------------------------------------*/

int GetMaxSubSum(int *Array, int left, int right)
{
int sum = 0;
int i;
    //递归停止条件
    if (left == right)
    {
        if (Array[left] > 0)
            sum = Array[left];
        else
            sum = 0;
    }
    else
    {
        int center = (left + right) / 2;
        //递归调用
        int leftSum = GetMaxSubSum(Array, left, center);
        int rightSum = GetMaxSubSum(Array, center + 1, right);

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

        int sum2 = 0;
        int rights = 0;
        for (i = center + 1; i <= right; i++)
        {
            rights = rights + Array[i];
            if (rights > sum2)
                sum2 = rights;
        }

        sum = sum1 + sum2;

        //情况1 
        if (sum < leftSum)
            sum = leftSum;

        //情况2
        if (sum < rightSum)
            sum = rightSum;

        return sum;
    }
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值