問題描述:給定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;
}
參考文獻: