問題描述:給定一個包含N個元素的數據A(A[0], A[1], A[2]...A[N-1]),這個數組天然有不少子數組,那麼,這些子數組之和的最大值是什麼?ios
Example: 數組[1, -2, 3, 5, -3, 2],最大的子數據是[3, 5],和爲8。算法
解法一:窮舉法數組
最直接,也是最簡單的方法,窮舉子數組A[i:j]的和,找出一個最大的,算法的時間複雜度O(N2),算法的代碼以下:spa
#include <iostream> #include <climits> using namespace std; int max_sub_sum(const int* A, int n) { int max_sum = INT_MIN; for (int i = 0; i < n; i++) { int sub_sum = 0; for (int j = i; j < n; j++) { sub_sum += A[j]; //sum(i,j) if ( max_sum < sub_sum) { max_sum = sub_sum; } } } return max_sum; } int main() { int A[] = {1, -2, 3, 5, -3, 2}; int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int)); cout<<"max sub sum:"<<max_sum<<endl; system("pause"); return 0; }
解法二:分治法code
將數組分紅兩個子數組:A[0:n/2]和A[n/2 + 1, n]那麼最大的組數據之和,必然出如今如下三種狀況;blog
1)最大子段與A[0:n/2]重合;遞歸
2)最大子段與A[n/2 + 1, n]重合it
3)最大子段誇過A[0:n/2]和A[n/2 + 1, n-1]兩段。io
對於1)、2)兩種狀況能夠遞歸解決,而對於第三種狀況,咱們只要找到A[0:n/2]以n/2爲終點最大和,A[n/2 + 1, n-1]以n/2 + 1爲起點的最大和,兩種相加便可。class
算法的時間複雜度爲O(NlogN),代碼以下:
#include <iostream> #include <climits> using namespace std; int max_sub_sum(int* A, int i, int j) { if (i == j) { return A[i]; } int mid = (i + j) / 2; int max_sum = max(max_sub_sum(A, i, mid), max_sub_sum(A, mid+1, j)); int left_mid_max_sum = INT_MIN; int mid_sum = 0; for (int k =mid; k >= i; k--) { mid_sum += A[k]; if (left_mid_max_sum < mid_sum) { left_mid_max_sum = mid_sum; } } int right_mid_max_sum = INT_MIN; mid_sum = 0; for (int k = mid+1; k <= j; k++) { mid_sum += A[k]; if (right_mid_max_sum < mid_sum) { right_mid_max_sum = mid_sum; } } max_sum = max(max_sum, left_mid_max_sum + right_mid_max_sum); return max_sum; } int main() { int A[] = {1, -2, 3, 5, -3, 2}; int max_sum = max_sub_sum(A, 0, (sizeof(A) / sizeof(int)) -1); cout<<"max sub sum:"<<max_sum<<endl; system("pause"); return 0; }
解法三:動態規劃
動態規劃問題依賴於兩個基本因素:1)最優子結構;2)重疊結構。首先定義兩個量All(i)和Start(i),All(i)表示從A[i:n-1]數組中最大的子段和,Start(i)則表示瞭如下表i做爲起始位置的最大子段和,那麼該問題的最優的遞歸表達式:
All(i) = max(All(i+1), start(i))
這個公式表示,All(i)取:從i下標開始的字段數組start(i),從i+1開始的最大字段和All(i+1)之間的最大值, start(i)的定義以下:
start(i) = max(A[i], A[i] + start(i+1))
該解法的代碼以下:
#include <iostream> #include <climits> using namespace std; int max_sub_sum(int* A, int n) { int all = A[n-1]; int start = A[n -1]; for (int i = n -2; i >= 0; i--) { start = max(A[i], A[i] + start); //start(i) = max(A[i], start(i+1)) all = max(all, start); } return all; } int main() { int A[] = {1, -2, 3, 5, -3, 2}; int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int)); cout<<"max sub sum:"<<max_sum<<endl; system("pause"); return 0; }
最後一種方法是淳樸的迭代算法。
迭代算法看起來簡單,可是這個問題但有着極其驚人的療效:1)用變量per_sum保存當前以i-1做爲結束下標的字段數組和,若是per_sum小於0(前面的部分起到了「負面」做用,須要捨棄),從下標i開始重新字段數組,於此同時,不斷比較每次字段數組的最大值,當迭代結束是,便會獲得字段數組的組大和。
#include <iostream> #include <climits> using namespace std; int max_sub_sum(int* A, int n) { int max_sum = INT_MIN; int sum = 0; for (int i = 0; i < n; i++) { if (sum < 0) //前面的元素提到了負面做用,須要捨棄 { sum = A[i]; } else { sum += A[i]; //前面的元素提到了正面做用,須要保留 } if (sum > max_sum) { max_sum = sum; } } return max_sum; } int main() { int A[] = {1, -2, 3, 5, -3, 2}; int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int)); cout<<"max sub sum:"<<max_sum<<endl; system("pause"); return 0; }