1. 程式人生 > >演算法設計——最大子段和問題分析

演算法設計——最大子段和問題分析

最大子段和問題——分治法應用

問題描述: 給定由n個整數(存在負整數)組成的序列 a1,a2,a3,,an ,求序列形式如k=ijak的子段和的最大值。當整個序列所有整數均為負整數時,其最大子段和為0。依次定義,所求的最大值為

max{0,max1ijnk=ijak} 例如:當(a1,a2,a3,a4,a5,a6)=(2,11,4,13,5,2)時,最大子段和為k=24ak

最大子段和問題的分治策略如下:

  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]的最大欄位和與k=ijak,且滿足1in/2,n/2+1jn
  2. 求解。第一種情況與第二種情況可以利用遞迴進行求解。對於第三種情況,經過分析得出,A[n/2]與A[n/2+1]在最優子序列中。因此可以在A[1…n/2]中計算出

    sum1=max1in/2k=in2ak,並在A[n/2+1…n]中計算出sum2=maxn/2ink=n/2iak,則最終計算出sum=sum1+sum2為第三種情況下的最優解。

  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;
    }
}