问题描述:给定n个整数(可能有负数)组成的序列a1,a2,...,an,求该序列的最大子段和。如果所有整数都是负数,那么定义其最大子段和为0。
方法一:暴力双重循环破解法
方法二:递归分治
在数组的 center = (right-left)/2+left 位置处分开。形成两个子数组。
那么,最大子段和 可能出现在三个位置:
a,可能出现在【左子数组】
b,可能出现在【右子数组】
c,可能出现在【过center的 中间某部分元素组成的子数组】。
下面考虑 三种情况的计算方法:
第一种情况: 计算 left 到 center 的最大和,记作 leftSum
第二种情况: 计算从 center+1 到 right的最大和,记作 rightSum
第三种情况: 跨边界的和。 ;以center为中心分别向两边计算和。
a.从 center出发,每次向左边扩张一步,并且记录当前的值S1,如果当前的和比上次的和大,就更新S1,一直向左扩张到 位置 Left。
b.从 center+1出发,每次扩张一步,计算当前的和 为S2,如果当前的值比上次的和 大,那么,就更新S2的值,一直向右扩张到位置Right。
c.计算过center的连续值的和,S1+S2的值 Sum。 这个就是跨边界的和。
上面三种情况考虑计算完成后,最后一步就是,比较三个值中的最大值,取最大值就可以了。
代码部分:
int MaxSubSum(int a[], int left, int right) {
if (right == left) //递归结束的标志
return a[left]>0?a[left]:0;
int center = (left + right) / 2;
int leftsum = MaxSubSum(a, left, center); //左子段的最大值
int rightsum = MaxSubSum(a, center+1, right); //右子段的最大值
int s1 = 0, s2 = 0, cur_t = 0;
for (int i = center; i >= 0; i--) { //center到left的连续的最大值
cur_t += a[i];
if (cur_t > s1)
s1 = cur_t;
}
cur_t = 0;
for (int i = center+1; i <= right; i++) { //center到right的连续的最大值
cur_t += a[i];
if (cur_t > s2)
s2 = cur_t;
}
int sum = s1 + s2; //sum是过center的连续的最大值
return leftsum > rightsum ? (leftsum > sum ? leftsum : sum) : (rightsum > sum ? rightsum : sum); //比较三者的大小,返回最大的那一个
}
方法三:动态规划
代码部分:
int MaxSubArray(int a[], int n) {
int i, b = 0, sum = 0;
for (i = 0; i < n; i++) {
if (b > 0) //b看作b[i-1],如果b是负数那肯定没有a[i]大 b=max(b[i-1]+a[i], a[i])
b += a[i];
else
b = a[i];
if (b > sum) //取得b的极值
sum = b;
}
return sum;
}
参考文献: