zoukankan      html  css  js  c++  java
  • 关于求已知整数数组的连续子数组的最大和的方法

    日期:2019.3.9

    博客期:040 

    星期六

      这次的标题就是题目——关于求已知整数数组的连续子数组的最大和的方法,打个比方:给予数组 { 1 , -2 , 3 , -1 , 0 , 2 } ,它的连续子数组的最大和就是取得 { 3 , -1 , 0 , 2 } 时的和 4 !就是说我们需要找到元素值和最大的子数组。我们大可以考虑几种方法:

      (1)先求出所有的子数组,再找出每一组的和,求出和的最大值 >>>>>>>(优化)>>>>>>>>求子数组的时候考虑负数的情况,并同时记录最大值

      (2)通过假设的方法,最大值一定存在于这个数组,所以先求出最大值,之后围绕最大值向两边延申使得问题得到解决>>>>>>>>>(经验证,方法错误)>>>>>>>>> { 5 , -1 , 7 , -20 , 9 } (取得11)

      (3)[个人思想方法]

        我们从输入的第一个正数开始,计算它们的和记作Tnum(正数和),直到输入的数是负数,开辟一个Fnum(负数和),记录从此之后的负数和,直到输入的下一个数是正数。此时,判断 Tnum + Fnum 的大小是否大于 0 ,[注:只有负数转正数的时候判断] ,大于的话就更新Tnum的值为Tnum + Fnum + p ( p 值为你本次输入的值) ,小于或等于的话取 Tnum 为 p 。在每一次的循环中记录 Tnum 和 p 最大值 ,也就是最后的结果了。

        [实例:{ -3 ,2 ,-1,0,3  ,3 ,-4,3 } ]

        我们从输入的第一个数开始,-3不是正数,跳过,2是正数,我们把它记录到 Tnum 中,Tnum = 2 ,之后直到输入的是负数,所以输入的 -1 是负数 ,我们把它记录到 Fnum 中,Tnum = 2 , Fnum = -1,之后输入的是 0 ,不是正数,记录到 Fnum中,Fnum = -1 + 0 = -1,再下一个输入的是 3 ,我们知道 3 是正数,所以进行判断,因为 Tnum + Fnum = 1 > 0 , 所以我们需要把 Tnum 更新成 Tnum + Fnum + p , 这样 Tnum = 2 + ( -1 ) + 3 = 4 , (如果第二个数是 1 ,我们就要把 Tnum 设成 3 了),之后继续,输入的 3 是正数,就继续 Tnum = 4 + 3 = 7 , 下一个是 -4 , 我们把 Fnum 更新成 -4 , [ 注 : Fnum 就是单纯的记录输入的两次正数之间的负数和 ],而且下一个是 3 ,我们知道 Tnum + Fnum > 0 , 所以Tnum =  7 + ( -4 ) + 3  = 6 , 最终我们得到了 p 的最大值 3 和 Tnum 的最大值 7 , 所以我们的结果就是 7 啦!

        实际结果:{ 2 , -1 , 0 , 3 , 3} 

        我的代码:  

     1 void My_Way()
     2 {
     3     //定义长度
     4     int n;
     5     //输入长度
     6     cin>>n;
     7     //最大值
     8     int rmax = -10000;
     9     //正数总值
    10     int Tnum = -10000;
    11     //负数总值
    12     int Fnum = 0;
    13     //记录是否发生转变
    14     int sis = 0;
    15     //标记是第几程度
    16     int attitude = 0;
    17     //循环
    18     for(int i = 0 ; i < n ; ++i)
    19     {
    20         int p;
    21         cin>>p;
    22         if(attitude==0)                    //---------------------------------------[寻找第一个正数]
    23         {
    24             if(p<=0)
    25                 ;
    26             else
    27             {
    28                 Tnum = p;
    29                 attitude = 1;
    30             }
    31         }
    32         else if(attitude==1)            //---------------------------------------[上一个数为正数]
    33         {
    34             if(p<0)
    35             {
    36                 if(sis==0)
    37                 {
    38                     sis = 1;
    39                     Fnum += p;
    40                 }
    41                 else
    42                     Fnum = p;
    43                 attitude = -1;
    44             }
    45             else
    46                 Tnum += p;
    47 
    48             if(Tnum>rmax)
    49                 rmax = Tnum;
    50         }
    51         else                            //---------------------------------------[上一个数为负数]
    52         {
    53             if(p>0)
    54             {
    55                 attitude = 1;
    56                 if(Tnum + Fnum > 0)
    57                     Tnum = (Tnum + Fnum) + p;
    58                 else
    59                     Tnum = p;
    60             }
    61             else
    62                 Fnum += p;
    63         }
    64         /*
    65         cout<<"p="<<p<<endl;
    66         cout<<"rmax="<<rmax<<endl;
    67         cout<<"(p>rmax)="<<(p>rmax)<<endl;
    68         */
    69         if(p>rmax)
    70             rmax = p;
    71         if(Tnum>rmax)
    72             rmax = Tnum;
    73     }
    74     cout<<rmax<<endl;
    75 }
    My_Way

        我的这种方法,其实还有可以优化的地方,比如对于关系的判断啊! attitude 只能取得 -1 , 0 , 1 三个值,而我们对于它的判定有以下概率问题;

        目前,我的方法空间复杂度较低(没有使用数组),时间复杂度为 O( n ) ;

      1、取1个值,没有实际意义

      2、取2个值:

        attitude的判断值 : 

        排列    0 1 -1     1 -1 0      ...

        <1> 1 1  查询3次   查询4次     ...

        <2> 1 -1  查询3次    查询4次     ...

        <3> -1 1  查询2次    查询6次     ...

        <4>-1 -1  查询2次    查询6次     ...

        对于attitude的判断次数的数学期望最小值为 2.5;

      3、取3个值

        attitude的判断值

        <1> 1 1 1    查询5次

        <2> 1 1 -1   查询5次

        <3> 1 -1 1     查询6次

        <4> 1 -1 -1         查询6次

        <5> -1 1 1     查询4次

        <6> -1 1 -1    查询4次

        <7> -1 -1 1    查询3次

        <8> -1 -1 -1   查询3次

        对于attitude的判断次数的数学期望最小值为 4.5;

       所以我写的应该是最优的判断顺序了!

      (4) 同学的宝贵方法

        取输入的第 i 个元素值为 a[i] , 还有前 i 项的和为 S[i] , 所以有:

       

        而我们也可以使用 S[n] 来表示 a[n] ;

        

        从而引导处真正的连续项和的公式:

        

        我们现在可以使用 a 数组储存我们输入的数,并同时计算前几项的和记录到数组 S 中。

        【初始想法】 之后只要找到 S 数组里的最大值和最小值,之后做差,不就可以得到最大值了。

        <问题>你可以想想如何求数组里和的最小值?就是最小值减去最大值?当然不是!因为必须要控制 i <= j , 使得求出来的是连续和。

        【改进方法】所以我们继续我们制作限定,使用 min_p 记录位于当前输入的数之前的最小的 S 数组里的值,使用 max_p 记录所有 ( p - min_p ) 中最大的值,记录到最后 max_p 就是所求的最大值了。

        我根据同学的思想,制作了C++代码:

        1、制作初步的实现

        

     1 void Friend_Way()
     2 {
     3     //原理 p[i] + p[i+1] + p[i+2} + ... + p[j-1] + p[j] = S[j] - S[i-1]
     4 
     5     int n;
     6     //n是总数组大小
     7     cin>>n;
     8     //储存初始数组
     9     int * q = new int [n];
    10     //储存前i项和,即 p[i] = q[0] + q[1] + q[2] + ... + q[i]
    11     int * p = new int [n+1];
    12     //预处理
    13     cin>>q[0];
    14     p[0] = q[0];
    15 
    16     //记录最大值
    17     int final_max = q[0];
    18     //记录p[i]目前达到的最小值
    19     int min_pos = 0;
    20 
    21     for(int i=1;i<n;++i)
    22     {
    23         cin>>q[i];
    24         p[i] = q[i] + p[i-1];
    25         if(p[min_pos]>=p[i-1])
    26         {
    27             min_pos = i-1;
    28         }
    29         if(p[i]-p[min_pos]>final_max)
    30             final_max = p[i] - p[min_pos];
    31     }
    32     cout<<endl;
    33     cout<<"最终结果:"<<final_max<<endl;
    34 }
    Friend_Way

        2、节约空间,不再使用 数组 S

        

     1 void Friend_Way_S()
     2 {
     3     int n;
     4     cin>>n;
     5     int * q = new int [n];
     6     cin>>q[0];
     7     //最终值
     8     int rmax = q[0];
     9     //和值
    10     int sum = q[0];
    11     //最小的值
    12     int min = q[0];
    13 
    14     for(int i=1;i<n;++i)
    15     {
    16         cin>>q[i];
    17         sum += q[i];
    18         if(sum-min>rmax)
    19             rmax = sum - min;
    20         if(min>sum)
    21             min = sum;
    22     }
    23     cout<<endl;
    24     cout<<"最终结果:"<<rmax<<endl;
    25 }
    Friend_Way_S

        3、节约空间,不再使用 数组 a

        

    void Friend_Way_SS()
    {
     int n;
     cin>>n;
     int p;
     cin>>p;
     //最终值
     int rmax = p;
     //和值
     int sum = p;
     //最小的值
     int min = p;
     for(int i=1;i<n;++i)
     {
      cin>>p;
      sum += p;
      if(sum-min>rmax)
       rmax = sum - min;
      if(min>sum)
       min = sum;
     }
     cout<<endl;
     cout<<"最终结果:"<<rmax<<endl;
    }

    如果大家还有什么疑问,欢迎给我留言,大家一起交谈。

        

        

      

  • 相关阅读:
    Django 之Redis配置
    python之类中如何判断是函数还是方法
    Anaconda 虚拟环境安装及应用
    【转载】IDEA:XML配置提示URI is not registered
    idea中配置xml不自动提示解决方案
    Java接口成员变量和方法默认修饰符
    [转载]java中Date,SimpleDateFormat
    intellij idea 的全局搜索快捷键方法
    【转载】使用IntelliJ IDEA提示找不到struts-default文件
    【转载】Jmeter分布式部署测试-----远程连接多台电脑做压力性能测试
  • 原文地址:https://www.cnblogs.com/onepersonwholive/p/10500200.html
Copyright © 2011-2022 走看看