zoukankan      html  css  js  c++  java
  • 编程之美2.14——求和最大子数组

    这里记录的是从《算法导论》看来的解法,待补充。

    【解法1】——递归,二分

    将数组分成左右两边,最大子数组出现的情况可能有:

    1)左半边

    2)右半边

    3)跨左右两边

    对于半边求最大子数组,又可以递归上述思维,所以难点在于写出跨左右两边的情况。

    代码:

    void find_max_cross_subarray(int src[], int low, int mid, int high, 
        int &left_max, int &right_max, int &subsum)
    {
        int sum=0;
        int left_sum=INT_MIN;
        for(int i=mid; i>=low; i--){
            sum+=src[i];
            if(sum>left_sum){
                left_sum=sum;
                left_max=i;
            }
        }
        sum=0;
        int right_sum=INT_MIN;
        for(int j=mid+1; j<=high; j++)
        {
            sum+=src[j];
            if(sum>right_sum){
                right_sum=sum;
                right_max=j;
            }
        }
        subsum=left_sum+right_sum;
    }
    void find_max_array(int src[], int low, int high, int &left_max, int& right_max, int& subsum)
    {
        if(low==high){
            left_max=low;
            right_max=high;
            subsum=src[low];
            return;
        }        
        int mid=(low+high)/2;
        int left_low, left_high, left_sum;
        int right_low, right_high, right_sum;
        int cross_low, cross_high, cross_sum;
        find_max_array(src, low, mid, left_low, left_high, left_sum);
        find_max_array(src, mid+1, high, right_low, right_high, right_sum);
        find_max_cross_subarray(src, low, mid, high, cross_low, cross_high, cross_sum);
        if(left_sum>=right_sum&&left_sum>=cross_sum){
            left_max=left_low;
            right_max=left_high;
            subsum=left_sum;
    
        }
        else if(right_sum>=left_sum&&right_sum>=cross_sum){
            left_max=right_low;
            right_max=right_high;
            subsum=right_sum;
        }
        else{
            left_max=cross_low;
            right_max=cross_high;
            subsum=cross_sum;
        }
        return;
    }

    评价:

    1)跨两边的情况,左边要从中间开始逆推,右边从中间开始顺推。

    2)每一趟遍历得到一个从当前位置开始的最大子数组,记录最大和,如果加上s[next]之后大于最大和,则更新,并记录next。

    【未成熟待验证方案】

    从sum=s[0],sum<0时,sum=s[next],即从下一个节点开始重新记录最大和,然后像上面第2点技巧遍历。

    当数组全是负数时,就找出全员最大值返回。

    int Maxsum_ultimate(int * arr, int size)
    {
        int maxSum = -INF;
        int sum = 0;
        for(int i = 0; i < size; ++i)
        {
            if(sum < 0)
            {
                sum = arr[i];
            }else
            {
                sum += arr[i];
            }
            if(sum > maxSum)
            {
                maxSum = sum;
            }
        }
        return maxSum;
    }

    评价:多么简单呀!参考博文:http://www.ahathinking.com/archives/120.html

    【解法2】——动态规划

    把问题分解为一个n-1规模的数组和一个当前考量元素。如考量第一个元素A[0],以及最大的一段数组(A[i]...A[j])和A[0]之间的关系,有下列几种情况:

    1.当0=i=j时,元素A[0]本身构成和最大的一段;

    2.当0=i<j时,和最大的一段以A[0]开始;

    3.当0<i时,元素A[0]跟和最大的一段没有关系。

    用start[i]记录包含A[i]的和最大数组的和,all[i]为从A[i]-A[N-1]和最大一段数组的和,从后向前遍历,就把时间缩到了O(n)!

    int find_max_array1(int n[], int length)
    {
        int *start=new int[length];
        int *all=new int[length];
        start[length-1]=n[length-1];
        all[length-1]=n[length-1];
        for(int i=length-2; i>=0; i--){
            start[i]=max(n[i], n[i]+start[i+1]);
            all[i]=max(start[i], all[i+1]);
        }
        delete [] start;
        delete [] all;
        return all[0];
    }

    还有改进,把空间也降为O(1)。

    int find_max_array1(int n[], int length)
    {
        nstart=n[length-1];
        nall=n[length-1];
        for(int i=length-2; i>=0; i--){
            nstart=max(n[i], n[i]+nstart);
            nall=max(start[i], nall);
        }
        return nall;
    }

    评价:这个简短到简直不敢想(╭ ̄3 ̄)╭♡

  • 相关阅读:
    WPF MarkupExtension
    WPF Binding小数,文本框不能输入小数点的问题
    WPF UnhandledException阻止程序奔溃
    .Net Core的总结
    C#单元测试
    Csla One or more properties are not registered for this type
    unomp 矿池运行问题随记
    矿池负载运行监测记录
    MySql 数据库移植记录
    后台服务运行后无故停止运行,原因不明
  • 原文地址:https://www.cnblogs.com/ketchups-notes/p/4464825.html
Copyright © 2011-2022 走看看