zoukankan      html  css  js  c++  java
  • 最大子段和

    原博链接 

     

    一. 问题适用方法

    给定长度为n的整数序列,a[1....n],求 [1,n] 某个子区间 [i,j]使得 a[i]+.....+a[j] 和最大,或者求出最大的这个和。例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为 [2,4]。

     

    二. 问题分析

    1.穷举法

    用两层for循环遍历所有的子区间。

    //穷举法 
    #include<bits/stdc++.h>
    int start=0; //起始位置 
    int end=0; //结束位置 
    int max=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int sum=0;
            for(int k=i;k<=j;k++)
            sum+=a[k];
            if(sum>max){
                start=i;
                end=j;
                max=sum;
            }
        }
    }

    时间复杂度是 O(n^3) 。这个代码还可以做优化,实际上我们并不需要每次都重新从起始位置求和加到终点位置,可以利用之前的计算结果。

    或者我们换一种穷举思路,对于起点 i ,我们遍历所有长度为 1,2,......,n-i+1 的子区间和,以求得和最大的一个,这样遍历了所有起点的不同长度的子区间,同时,对于相同起点的不同长度的子区间,可以利用前面的计算结果来计算后面的。

    比如,i为起点长度为2的子区间和就等于长度为1的子区间的和加上a[i+1]即可,这样就减少了一个循环,时间复杂度 O(n^2)。

    //优化
    
    int start=0;
    int end=0;
    int max=0;
    for(int i=1;i<=n;i++){
        int sum=0;
        for(int j=i;j<=n;j++){
            sum+=a[j];
            if(sum>max){
                start=i;
                end=j;
                max=sum;
            }
        }
    } 

     

    2.分治法

    求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start,end]只可能有以下三种可能:

    1.在[1,n/2]这个区域

    2.在[n/2+1,n]这个区域

    3.起点位于[1,n/2],终点位于[n/2+1,n]

     

    以上三种情况的最大值就是所求的,前两种符合子问题的递归性,所以可以递归,第三种则需要单独处理,第三种情形包括了 n/2,n/2+1两个位置,这样可以利用第二种穷举的思路求出:

    1.以n/2为终点,往左移动扩张,求出和最大的一个left_max

    2.以n/2+1为起点,往右移动扩张,求出和最大的一个right_max

    3.left_max+right_max是第三种情况可能的最大值

     

    int maxInterval(int a,int left,int right){
        if(right==left) return a[left]>0?a[left]:0;
        int center=(left+right)/2; 
        int leftMaxInterval=maxInterval(a,left,center); //左边区间的最大子段和
        int rightMaxInterval=maxInterval(a,center+1,right); //右边区间的最大子段和
    
        // 以下求端点分别位于不同部分的最大子段和
        
        //center开始向左移动
        int sum=0;
        int left_max=0;
        for(int i=center;i>=left;--i){
            sum+=a[i];
            if(sum>left_max)
            left_max=sum;
        } 
        //center+1开始向右移动
        sum=0;
        int right_max=0;
        for(int i=center+1;i<=right;i++){
            sum+=a[i];
            if(sum>right_max)
            right_max=sum;
        } 
        int ret=lefr_max+right_max;
        if(ret<leftMaxInterval)
        ret=leftMaxInterval;
        if(ret<rightMaxInterval)
        ret=rightMaxInterval;
        return ret;
    }

     

    这种算法的时间复杂度为 O(nlogn)

    3.动态规划法

    由于是一个连续的区间,所以可以这样思考

    1.令b[j]表示以位置 j 为终点的所有子区间中和最大的一个。

    2.子问题:如j为终点的最大子区间包含了位置 j-1,则以 j-1 位终点的最大子区间必然包括在其中。

    3.如果 b[j-1]>0,那么显示 b[j]=b[j-1]+a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含。

    4.如果 b[j-1]<=0,那么b[j]=a[j],因为既然最大,前面的负数必然不能使你更大。

    //动态规划
    int max=0;
    int b[n+1];
    int start=0,end=0;
    memset(b,0,n+1);
    for(int i=1;i<=n;i++){
        if(b[i-1]>0){
            b[i]=b[i-1]+a[i];
        }
        else{
            b[i]=a[i];
        }
        if(b[i]>max)
        max=b[i];
    } 

    时间复杂度 O(n)。

     

     

     

     

     

     

  • 相关阅读:
    从Prism中学习设计模式之Event Aggregator 模式
    Apache的HttpClient调用Https忽略证书校验
    SaltStack安装及API开启
    Java连接WebSocket服务忽略证书校验
    SpringBoot实现WebSocket服务
    MySQL主从复制搭建
    Matlab基础知识(持续更新中)
    FIFO基础知识
    图像常识知识
    VC调试记录(持续更新中)
  • 原文地址:https://www.cnblogs.com/wushengyang/p/10834745.html
Copyright © 2011-2022 走看看