经典算法题之(四)------ 最大子序列和

本文深入探讨了最大子序列和问题的高效算法,通过直观的比喻解释了算法原理,如孤岛与大海,以及如何通过扫描算法找到序列中最大和的子序列。同时,文章还讨论了算法的局限性,即只知道最大子序列的和,但无法确定其具体位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

       最大子序列和问题

 

       最优的算法逻辑很简单,扫描,如果和>0就继续扫描,刷新一次最大和,如果<0就置0重新累积和:


        int Sum=0;
        int MaxSum;
        for (int i = 0; i < array.size(); ++i)
        {
            if(Sum<=0)
                Sum=array[i];
            else
                Sum+=array[i];
            if(Sum>MaxSum)
                MaxSum=Sum;
        }
        return MaxSum;

         它基于这样一个事实:假设A[i...j]是最大和子序列,暂且叫L,则

1)其左侧和右侧(A[i-1],A[j+1],如果存在的话)定然是一个负数,并且其左侧任何一个带A[i-1]或其右侧任何一个带A[j+1]的连续子序列的和必然<=0。

(证明很容易,如果不成立则一定加入了最大和子序列中了)

 

2)A[i] 和 A[j]一定是正数,不然肯定被舍弃了。

 

3)再进一步,从最大子序列的左侧往右侧看,L任意从A[i]开始的连续子序列和必然都是正数,如果考虑到A[i]也是正数,则可以这么说,将L随机“劈”成两半,左侧的和必然>0,不然

       左侧和+右侧和 = 最大子序列和

       若左侧和<0,则右侧和>最大子序列和,那最大子序列就是右侧部分了,没必要加上左侧这个累赘。

       其实右侧也一样。这样一来,随机劈两半,两半必>0。

 

 

       有了上面的积累,我们可以将最大和子序列比作海上的“孤岛”,虽然孤岛上可能还有小水潭,但是总体上是“正的”,任意劈成两半,两半的“和”也是正的(土可以填住水潭)。而孤岛两侧都是“大海”,可能偶尔有一两个“峰顶”,但是不足以填满和孤岛之间的海域。

     (其实就是上述程序中累加和Sum和i轴围成的图形,我也不知道为什么要说得这么复杂。。)

 

       为了找到海上最大的这么一座岛,我们可以这么干,坐着摩托艇从起点出发,如果遇到能填住海水的小岛,我们就拿出照相机拍照刷出来,和上一次拍的小岛比较一下,如果比以前岛要大,就把以前照片扔了,留下这一张(泳裤口袋太小只能装一张照片)。

      这样只要泥土还是多于海水就一直拍照比较一下,一旦发现海水多于泥土了就重新寻找。

       这样我们一定可以越过只散布着零零星星泥土的海域,成功找到最大的岛。(如果只从寻找最大岛这一目的,其实之前的路程都是为了赶路,找到坑坑洼洼之后的大岛)

       但是,对于上面的算法,我们只能做到将最大岛的风景“拍”下来,然后继续前进,因为在到达终点之前我们是不知道这里就是最大的岛,要继续往前填海填土试探,如果海水淹没了所有土地,则计零重新开始。实际上我们之前越过零星泥土的海域都是这么干的,当时我们也将当时的泥土认为是最大的岛,并拍下岛的风景,当遇到海水淹没再前进,遇到更大的岛再拍新的风景(照相机只能保存一张图片)。

 

        所以,明显也能看出上述的算法的缺点是,到达终点时,只能留下最大岛的风景,却不知岛的位置。即知道最大子序列的和值是多少,却不知道最大和子序列是什么。

        如果现在换一个问题:求一个序列的最大子序列。

        则很明显,要加两个笔记本(笔记本很特殊,只能记录一个数字),每次重新记录,看到小岛时,记下将当前小岛位置记录为“最大岛的起点和终点”,如果往后发现岛在变大(岛后面原来有更大的岛),就将终点更新,否则(变小或者直接被海水淹没)终点不变。

         talk is cheap,show codes:

        int Sum = 0;        // 照相机
        int MaxSum;         // 口袋里的照片

        int start,final;    // 两个笔记本

        for (int i = 0; i < array.size(); ++i)
        {
            if(Sum<=0)
          {
             Sum = array[i];
             start = i;
             final = i;
          }   
            else
                Sum+=array[i];
            if(Sum>MaxSum)
          {
             MaxSum=Sum;
             final = i;      // 重新记录小岛的终点
          }
                
        }
        return MaxSum,start,final;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值