C6-1 最大子數組和
題目描述
給定一個數組a[0,...,n-1],求其最大子數組(長度>=1)和
輸入描述
第一行一個整數n(1<=n<=5000),然後依次輸入n個整數(每個整數範圍[-5000, 5000])
輸出描述
輸出一個整數表示最大子數組和
樣例輸入
5 1 -1 1 1 -1
樣例輸出
2
思路;因為這道題讓我們求和,那我們設置個count用於計算到目前為止的和的值,如果>0繼續,若<0,則應當拋棄,否則,會影響以後的值;
1 #include <iostream> 2 #include <vector> 3 using namespace std;4 5 int max_sum(const vector<int> &array); 6 7 int main(){ 8 int n; 9 cin>>n; 10 if(n<0||n>5000) return false; 11 vector<int>array(n); 12 for(int i=0;i<n;i++){ 13 cin>>array[i]; 14 if(array[i]<-5000||array[i]>5000) return false; 15 } 16 cout<<max_sum(array)<<endl; 17 return 0; 18 } 19 20 int max_sum(const vector<int> &array){ 21 int sum=0; 22 int count=0; 23 for(int i=0;i<array.size();i++){ 24 count+=array[i]; //將所輸入的數組元素賦值給count計數 25if(count>sum) //若此時count>sum則將count賦值給sum 26 sum=count; 27 if(count<0) 28 count=0; //若和為0,則將此時count置0; 29 } 30 return sum; 31 }
解決這道題方法還有很多的方法
法1:暴力枚舉法
此種方法最簡單,
記sum[i..j]為數組中第i個元素到第j個元素的和(其中0<=i<j<=n-1),通過遍歷所有的組合之和,就能找到最大的一個和了。
偽代碼如下:
int maxSubArray(int *A,int n) { int maxium = -1; //保存最大子數組之和 for i=0 to n-1 do sum = 0; //sum記錄第i到j的元素之和 for j=i to n-1 do sum += A[j]; if sum>maxium do //更新最大值 maxium = sum; return maxium; }
此種方法的時間復雜度為O(n2),顯然不是一種很好的辦法。
方法二:分支界定
這裏再介紹一種更高效的算法,時間復雜度為O(nlogn)。這是個分治的思想,解決復雜問題我們經常使用的一種思維方法——分而治之。
而對於此題,我們把數組A[1..n]分成兩個相等大小的塊:A[1..n/2]和A[n/2+1..n],最大的子數組只可能出現在三種情況:
A[1..n]的最大子數組和A[1..n/2]最大子數組相同;
A[1..n]的最大子數組和A[n/2+1..n]最大子數組相同;
A[1..n]的最大子數組跨過A[1..n/2]和A[n/2+1..n]
前兩種情況的求法和整體的求法是一樣的,因此遞歸求得。
第三種,我們可以采取的方法也比較簡單,沿著第n/2向左搜索,直到左邊界,找到最大的和maxleft,以及沿著第n/2+1向右搜索找到最大和maxright,那麽總的最大和就是maxleft+maxright。
而數組A的最大子數組和就是這三種情況中最大的一個。
偽代碼如下:
int maxSubArray(int *A,int l,int r) { if l<r do mid = (l+r)/2; ml = maxSubArray(A,l,mid); //分治 mr = maxSubArray(A,mid+1,r); for i=mid downto l do search maxleft; for i=mid+1 to r do search maxright; return max(ml,mr,maxleft+maxright); //歸並 then //遞歸出口 return A[l]; }
方法三:動態規劃
這算是一個經典的動態規劃的題目了,如果不知道動態規劃可以先不去理解這個名詞。用通俗點的語言描述這個算法就是:
令cursum(i)表示數組下標以i為起點的最大連續下標最大的和,而maxsum(i)表示前i個元素的最大子數組之和。那麽我們就可以推出下一個maxsum(i+1)應該為cursum(i+1)和maxsum(i)中選取一個最大值。遞推式為:
cursum(i) = max{A[i],cursum(i-1)+A[i]};
maxsum(i) = max{maxsum(i-1),cursum(i+1)};
偽代碼為:
int maxSubArray(int *A,int n) { cursum = A[0]; maxsum = A[0]; for i=1 to n-1 do /*當我們加上一個正數時,和會增加;當我們加上一個負數時,和會減少。如果當前得到的和是個負數,那麽這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。*/ if cursum<0 do cursum = 0; cursum += A[i]; if cursum>maxsum do maxsum = cursum; return maxsum; }
這種算法時間復雜度只是O(n),效果非常好!
#include <iostream> #include <iterator> #include <algorithm> #include <cstdlib> #include <ctime> using namespace std; const int INF=0x7fffffff; int max_sub_array(int arr[],int n,int &left,int &right) { int maxium=-INF; int sum; for(int i=0;i<n;i++){ sum=0; for(int j=i;j<n;j++){ sum+=arr[j]; if(sum>maxium){ maxium=sum; left=i; right=j; } } } return maxium; } int max_sub_array(int arr[],int l,int r,int &left,int &right) { if(l<r){ int mid=(l+r)/2; int ll,lr; int suml=max_sub_array(arr,l,mid,ll,lr); int rl,rr; int sumr=max_sub_array(arr,mid+1,r,rl,rr); int sum_both=0; int max_left=-INF; int ml,mr; for(int i=mid;i>=l;i--) { sum_both+=arr[i]; if(sum_both>max_left){ max_left=sum_both; ml=i; } } int max_right=-INF; sum_both=0; for(int i=mid+1;i<=r;i++) { sum_both+=arr[i]; if(sum_both>max_right){ max_right=sum_both; mr=i; } } sum_both=max_left+max_right; if(suml<sumr) { if(sumr<sum_both) { left=ml; right=mr; return sum_both; } else { left=rl; right=rr; return sumr; } } else{ if(suml<sum_both) { left=ml; right=mr; return sum_both; } else { left=ll; right=lr; return suml; } } } else { left=l; right=r; return arr[l]; } } int max_sub_array_(int arr[],int n,int& left,int&right) { int cursum=arr[0]; int maxsum=arr[0]; int pos=0; pos=0; for(int i=1;i<n;i++) { // if(cursum<0) // cursum=0; cursum+=arr[i]; if(cursum<arr[i]) { pos=i; cursum=arr[i]; } if(cursum>maxsum) { maxsum=cursum; left=pos; right=i; } } return maxsum; } void test1() { int arr[]={-2,5,3,-6,4,-8,6}; int len=sizeof(arr)/sizeof(arr[0]); int left,right; int sum; cout<<"arr:"; copy(arr,arr+len,ostream_iterator<int>(cout, " ")); cout<<endl; sum=max_sub_array(arr,len,left,right); cout<<"method1:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; sum=max_sub_array(arr,0,len-1,left,right); cout<<"method2:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; sum=max_sub_array(arr,len,left,right); cout<<"method3:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; } void test2() { const int LEN=10; int arr[LEN]; int sign[LEN]; srand(time(0)); for(int i=0;i<LEN;i++){ int val=rand()%1000; if(val%2==0) sign[i]=1; else sign[i]=-1; } for(int i=0;i<LEN;i++){ int val=rand()%100; arr[i]=val*sign[i]; } int left,right; int sum; int len=LEN; cout<<"arr:"; copy(arr,arr+len,ostream_iterator<int>(cout, " ")); cout<<endl; sum=max_sub_array(arr,len,left,right); cout<<"method1:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; sum=max_sub_array(arr,0,len-1,left,right); cout<<"method2:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; sum=max_sub_array(arr,len,left,right); cout<<"method3:("<<left<<","<<right<<") "; cout<<"sum="<<sum<<endl; } int main() { test2(); }
參考:http://www.cnblogs.com/xkfz007/archive/2012/05/17/2506299.html
C6-1 最大子數組和