★實驗任務:爲了打破進了實驗室就嫁不出去的詛咒,六一兒童節這天集訓隊特意舉辦了一場相親大會,來自各個學院的n個姑娘在實驗室內站成一排。每一個姑娘有本身的顏值ai。單身狗們決定邀請顏值之和最高的k個(k要大於0)位置相鄰的姑娘一塊兒晚上的狼人殺。
★數據輸入:輸入第一行爲一個數n(1<=n<=100000)表示姑娘個數。接下來一行有n個整數ai(-1000<=ai<=1000)表示第i個姑娘的顏值。
★數據輸出:輸出一行爲最大連續子串和。
輸入示例:5 6 -1 5 4 -7
輸出示例:14
輸入示例:7 0 6 -1 1 -6 7 -5
輸出示例:7ios
// // main.cpp // MaxSubSequence // // Created by wasdns on 16/8/31. // Copyright © 2016年 wasdns. All rights reserved. // #include <iostream> #include <cstdio> #include <string.h> #include <string> #include <algorithm> using namespace std; int number[1000005]; int MaxSubSequence(int n) //複雜度爲O(n^2) { int MaxSum = 0; for(int i = 0; i < n; i++) { int Cal = 0; for(int j = i; j < n; j++) { Cal += number[j]; if(Cal > MaxSum) //利用先前計算的結果進行比較 { MaxSum = Cal; } } } return MaxSum; }; int main() { int n, i; cin >> n; for(i = 0; i < n; i++) { cin >> number[i]; } int MaxSum = MaxSubSequence(n); cout << MaxSum << endl; return 0; }
// // main.cpp // MaxSubSequence_2 // // Created by wasdns on 16/8/31. // Copyright © 2016年 wasdns. All rights reserved. // #include <iostream> #include <cstdio> #include <string> #include <string.h> #include <algorithm> using namespace std; int number[1000005]; int MaxCalculator(int left, int right); int MaxSubSequence(int left, int right) { return MaxCalculator(left, right); } int CalMax(int a, int b, int c) { if(a > b) { if(a > c) return a; else return c; } else { if(b > c)return b; else return c; } } int MaxCalculator(int left, int right) { if(left == right) { if(number[left] > 0) return number[left]; else return 0; } int MaxLeftSum = 0; int MaxRightSum = 0; int middle; //cout << left << " " << right << endl; middle = (left + right)/2; //cout << middle << endl; MaxLeftSum = MaxCalculator(left, middle); MaxRightSum = MaxCalculator(middle + 1, right); //沒有+1:致使 0 1 循環 int MLASum = 0; //MaxLeftAreaSum int MRASum = 0; //MaxRightAreaSum int MSum = 0; for(int i = middle; i >= left; i--) { MSum += number[i]; if(MSum > MLASum) MLASum = MSum; } MSum = 0; for(int i = middle + 1; i <= right; i++) { MSum += number[i]; if(MSum > MRASum) MRASum = MSum; } return CalMax(MaxLeftSum, MaxRightSum, MLASum + MRASum); } int main() { int n, i; cin >> n; for(i = 0; i < n; i++) { cin >> number[i]; } cout << MaxSubSequence(0, n-1) << endl; return 0; }
// // main.cpp // MaxSubSequence_3 // // Created by wasdns on 16/8/31. // Copyright © 2016年 wasdns. All rights reserved. // #include <iostream> #include <cstdio> #include <algorithm> #include <string> #include <string.h> using namespace std; int Number[100005]; int b[100005]; //含當前位置元素的最大子序列和 不斷的更新 int MSS(int n) //狀態轉移方程:b[i] = MAX{b[i-1] + a[i], a[i]}; { memset(b, 0, sizeof(b)); int i; int sum = Number[0]; //sum 初始化爲 Number[0] b[0] = Number[0]; for(i = 1; i < n; i++) { if(b[i-1] + Number[i] > Number[i]) { b[i] = b[i-1] + Number[i]; } else b[i] = Number[i]; //cout << "b[i] = " << b[i] << endl; if(sum < b[i]) sum = b[i]; //cout << "sum = " << sum << endl; } return sum; } int main() { int n; cin >> n; for(int i = 0; i < n; i++) { cin >> Number[i]; } int MaxSequenceSum = MSS(n); cout << MaxSequenceSum << endl; return 0; }
給出的第一種解決代碼,首先複雜度相比使用三個for循環的O(N^3)降低了不少,可是仍然達不到要求。利用的是 以前計算的結果,從當前位置一個一個加過去。算法
實現算法:數組
int MaxSubSequence(int n) //複雜度爲O(n^2) { int MaxSum = 0; for(int i = 0; i < n; i++) { int Cal = 0; for(int j = i; j < n; j++) { Cal += number[j]; if(Cal > MaxSum) //利用先前計算的結果進行比較 { MaxSum = Cal; } } } return MaxSum; };
第一種易於理解,可是算法複雜度太高。所以爲了適應題目的要求,提到了第二種的解決方法。spa
算法實現:最大子序列和,要麼出如今 1)left 和 middle 之間,要麼出現2)在 middle 和 right 之間,還有 3)middle在這個子序列中。一共三種狀況,分別計算出三種狀況的最大子序列的大小,取最大值。code
前面兩種方法,可使用 遞歸分治 的思想:更新middle -> 計算上面三種狀況的子序列大小 -> 利用遞歸 -> 更新middle ···
當最後 left 和 middle 重合的時候(或者 middle 和 right 重合的時候),判斷 number[left] 是否大於0,大於0返回number[left],小於0返回0。遞歸
當利用遞歸 計算完成1)和2)的值以後,轉而計算3)的值:左邊從middle開始遍歷,找到left;右邊從middle+1開始遍歷,找到right;兩邊的值相加便可求得3)。dns
實現代碼:ci
int CalMax(int a, int b, int c) { if(a > b) { if(a > c) return a; else return c; } else { if(b > c)return b; else return c; } } int MaxCalculator(int left, int right) { if(left == right) //遞歸終止的條件 { if(number[left] > 0) return number[left]; else return 0; } int MaxLeftSum = 0; int MaxRightSum = 0; int middle; //cout << left << " " << right << endl; middle = (left + right)/2; //cout << middle << endl; MaxLeftSum = MaxCalculator(left, middle); MaxRightSum = MaxCalculator(middle + 1, right); //注意!沒有+1:致使 0 1 循環 int MLASum = 0; //MaxLeftAreaSum int MRASum = 0; //MaxRightAreaSum int MSum = 0; for(int i = middle; i >= left; i--) //從middle左邊開始遍歷 { MSum += number[i]; if(MSum > MLASum) MLASum = MSum; } MSum = 0; for(int i = middle + 1; i <= right; i++) //從middle+1右邊開始遍歷 { MSum += number[i]; if(MSum > MRASum) MRASum = MSum; } return CalMax(MaxLeftSum, MaxRightSum, MLASum + MRASum); //取三種狀況的最大值 }
第三種,即最多見的動態規劃問題了。動態規劃是一種利用以前計算結果的算法,咱們這裏使用了b[i]數組來存儲:b[i]表明的意思是,通過number[i]的最大子序列。
若是b[i-1]+number[i]大於number[i],那麼加到此處的最大子序列b[i]更新爲b[i-1]+number[i];不然,將b[i]更新爲number[i]從新開始。記錄整個過程當中的最大子序列和sum。狀態轉移方程:b[i] = MAX{b[i-1]+number[i], number[i]}
注意:sum須要初始化爲Number[0]。string
實現代碼:it
int MSS(int n) //狀態轉移方程:b[i] = MAX{b[i-1] + a[i], a[i]}; { memset(b, 0, sizeof(b)); int i; int sum = Number[0]; //sum 初始化爲 Number[0] b[0] = Number[0]; for(i = 1; i < n; i++) { if(b[i-1] + Number[i] > Number[i]) { b[i] = b[i-1] + Number[i]; } else b[i] = Number[i]; //cout << "b[i] = " << b[i] << endl; if(sum < b[i]) sum = b[i]; //cout << "sum = " << sum << endl; } return sum; }
2016/8/31