zoukankan      html  css  js  c++  java
  • 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计数 
    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

    雄关不惧 成败自含香
  • 相关阅读:
    oracle误操作commit之后,可以闪回数据
    删除表中重复数据,只保留一条数据
    去掉表中字段空的空格或换行符
    oracle主键自增
    List
    Collection Iterator 迭代器
    oracle 处理表的一列
    java swing
    oracle 常用函数大全
    cf 40A
  • 原文地址:https://www.cnblogs.com/cswangchen/p/7397186.html
Copyright © 2011-2022 走看看