最大子段和

本文主要介绍了最大子段和的三种算法,比较时间性能

最大子段和

  • 一.实验题目

    给定由n个整数组成的序列(a1, a2, …, an),求该序列形如 的子段和的最大值,当所有整数均为负整数时,其最大子段和为0

  • 二.实验目的

    • 深刻掌握动态规划法的设计思想并能熟练运用
    • 理解这样一个观点:同样的问题可以用不同的方法解决,一个好的算法是反复努力和重新修正的结果
  • 三.实验要求

    • 分别用蛮力法、分治法和动态规划法设计最大子段和问题的算法
    • 比较不同算法的时间性能
    • 给出测试数据,写出程序文档
  • 四.算法实现分析及结果

    • 蛮力法

      • #include<bits/stdc++.h>
        using namespace std;
        int ans,m;
        void sum(vector<int> v)
        {
            for(int i=0;i<v.size();i++){
                if(v[i]<0) continue;
                else{
                    ans=0;
                    for(int j=i;j<v.size();j++){
                        ans+=v[j];
                        if(m<ans) m=ans;
                    }
                }
            }
        }
        int main()
        {
            vector<int> v;
            int N;
            cin>>N;
            for(int i=0;i<N;i++){
                int a;
                cin>>a;
                v.push_back(a);
            }
            sum(v);
            cout<<m;
            return 0;
        }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49

        + 算法分析

        + 算法思想

        遍历,从每一个非0的位置开始,将后面的数累加,如果累加和小于之前所计算的最大值,则ans记录为该值,直到循环结束

        + 时间复杂度分析

        最大时间复杂度为循环n次,每次循环中再循环n-i次---->O(n)=n*(n-1)/2---->O($n^2$)

        + 实验结果分析

        {% asset_img 最大子段和暴力.png 最大子段和暴力%}

        + 运行结果

        {% asset_img 最大字段和暴力运行结果.png 最大字段和暴力运行结果%}

        + 分治法

        + ```c++
        int MaxSum(int a[],int left,int right)
        {
        int sum=0,midSum=0,leftsum=0,rightsum=0;
        int center,s1,s2,lefts,rights;
        if(left==right) sum=a[left];
        else{
        center=(left+right)/2;
        leftsum=MaxSum(a,left,center);
        rightsum=MaxSum(a,center+1,right);

        s1=0;lefts=0;
        for(int i=center;i>=left;i--){
        lefts+=a[i];
        s1=max(s1,lefts);
        }

        s2=0;rights=0;
        for(int j=center+1;j<=right;j++){
        rights+=a[j];
        s2=max(s2,rights);
        }

        midSum=s1+s2;
        sum=max(midSum,max(leftsum,rightsum));
        }
        return sum;
        }
      • 算法分析

        • 算法思想

          将序列划分为两份,递归求解左右两部分,然后计算一部分再左面,另一部分在右面的情况,最后选取三者最小即为ans

        • 时间复杂度分析

          当n==1时 T(n)=1

          当n>1时 T(n)=2*T(n/2)+1—->O(n $log_2n$)

      • 实验结果分析

      • 运行结果

    • 动态规划法

      • //只要前面的数的和不小于零加上下一个一定比下一个大
        int MaxSum(int a[],int n)
        {
            int pre=0;
            int sum=0;
            for(int i=0;i<n;i++){
                if(pre<=0) pre=a[i];
                else pre+=a[i];			
                sum=max(sum,pre);
            }
            return sum;
        } 
        
      • 算法分析

        • 算法思想

          满足最优性原理,设a 1,a 2,a 3,–,an是最长字段是起始位置到n的最长字段和,如果b 1, b 2,b 3,–,b n是起始位置到n-1的最大字段和,则b 1到an的和要大于a 1到a n,从而导致矛盾,所以满足最优性原理

          状态方程:$b[j]=max(\sum_{k=i}^{j}a[k]) 0<=i<j$

          ——>>>当b[j-1]>0时,b[j]=b[j-1]+a[j],else b[j]=a[j] 选取其中的最值

          只与前一项有关,用pre记录b[j-1]

        • 时间复杂度

          只需一层循环遍历一次—->O(n)

      • 实验结果分析

      • 运行结果

  • 五.实验体会

    计算最大子段和,首先利用了暴力法求解,两层循环结束,思路清晰,时间复杂度为平方级,然后用分治法,递归求解两部分,在两部分和公共部分三段中取最大值,即为结果时间复杂度为n*log级,最后是动态规划法,重点在于找到合适的状态转移方程,递推下去求出结果。动态规划型题首先得证明最优性原理,说明可以先求解子问题,该子问题解确定一个阶段,下一阶段解可以利用这一结果,避免了大量的重复计算.


最大子段和
http://kaikai12321.github.io/2022/06/09/最大子段和/
作者
Hou Kai
发布于
2022年6月9日
许可协议