最大子段和问题——分治法应用
问题描述:
给定由n个整数(存在负整数)组成的序列
a1,a2,a3,……,an
a
1
,
a
2
,
a
3
,
…
…
,
a
n
,求序列形式如
∑jk=iak
∑
k
=
i
j
a
k
的子段和的最大值。当整个序列所有整数均为负整数时,其最大子段和为0。依次定义,所求的最大值为
例如:当 (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
最大子段和问题的分治策略如下:
分解。如果将给定的序列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 ,且满足 1≤i≤n/2,n/2+1≤j≤n 1 ≤ i ≤ n / 2 , n / 2 + 1 ≤ j ≤ n 。
求解。第一种情况与第二种情况可以利用递归进行求解。对于第三种情况,经过分析得出,A[n/2]与A[n/2+1]在最优子序列中。因此可以在A[1…n/2]中计算出 sum1=max1≤i≤n/2∑n2k=iak s u m 1 = max 1 ≤ i ≤ n / 2 ∑ k = i n 2 a k ,并在A[n/2+1…n]中计算出 sum2=maxn/2≤i≤n∑ik=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种情况下的最大子段和,获得三者之中的最大值即为问题的求解。
根据以上分析我们可以设计出求解最大子段和的分治算法如下:
/*--------------最大子段和问题 分治法应用-----------------------------------
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;
}
}