1. 程式人生 > >C6-1 最大子數組和

C6-1 最大子數組和

輸出 思維 stream htm iter 思想 include 語言 連續

題目描述

給定一個數組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計數 25
if(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 最大子數組和