最大子段和 题解

又来水题解了

** 题目传送门**

又是一道经典的 dp

实际上这题也没有大家想的辣么难啊……

首先还是从不同的算法分析吧:

- 暴力

爆搜这代码简单直白。实际上我们需要枚举l和r,然后计算[l,r]的元素和就阔以了。

** 代码五分钟,运行两小时\color{purple}\text{代码五分钟,运行两小时} **

所以我们需要一个更高效的算法。


推导一下:\color{red}\text{推导一下:}

首先,我们现在纸上写一写:

** 2、-4、3、-1、2、-4、3。答案4(选3、-1、2)**

那么是怎么推出来的呢?

首先看到第一个数,是2\color{green}\text{2}

2\color{green}\text{2}后面是-4\color{green}\text{-4},所以如果-4\color{green}\text{-4}是答案的一部分,那么2\color{green}\text{2}一定也要加上去(答案就增加了)

随后是3\color{green}\text{3}

如果3\color{green}\text{3}把前面的2\color{green}\text{2}-4\color{green}\text{-4}加上去,结果是1\color{brown}\text{1}。这个时候反而比原来的3\color{green}\text{3}要小了。所以如果答案含有3\color{green}\text{3},就一定不会加上前面的2\color{green}\text{2}-4\color{green}\text{-4}

下一个数是-1\color{green}\text{-1}。这个数加上前面的3\color{green}\text{3}之后增加了数值(变成了2\color{brown}\text{2}),所以如果答案有-1\color{green}\text{-1},辣么绝对还有前面的3\color{green}\text{3}

接下来是2\color{green}\text{2},如果2\color{green}\text{2}加上前面的序列(3、-1)\color{green}\text{(3、-1)},辣么它的值变为4\color{brown}\text{4}。比原先增加了。

然后是-4\color{green}\text{-4},如果把-4\color{green}\text{-4}加上前面的序列(3、-1、2)\color{green}\text{(3、-1、2)},结果会变成0\color{brown}\text{0},比原先的-4\color{green}\text{-4}大,所以如果-4\color{green}\text{-4}是答案的一部分,那么前面的三个数也一定是答案的一部分。

最后一个数,也就是3\color{green}\text{3},如果将3\color{green}\text{3}加上前面的序列,结果变成了3\color{brown}\text{3}

最后我们来看一看刚推导的结果,发现4\color{brown}\text{4}是我们可以得出的最大和。

故输出4

所以说了这么多 (还用了这么多Markdown改颜色) ,最终的结果是什么呢?

- 第一个数为一个有效序列\color{DarkOrchid}\text{- 第一个数为一个有效序列}
- 如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。\color{DarkOrchid}\text{- 如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。}
- 如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列\color{DarkOrchid}\text{- 如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列}

在处理的过程中计算有效序列的所有元素之和。

最后取最大值即可。

(如果还有人不懂,我也只能贴代码了)

#include<bits/stdc++.h>
using namespace std;
int n,a[200020],b[200020],i,ans=-2147483647;
int main(){
   cin>>n;
   for(i=1;i<=n;i++){
       cin>>a[i];
       if(i<2) b[i]=a[i];
       else b[i]=max(a[i],b[i-1]+a[i]);
       ans=max(ans,b[i]);
   }
   cout<<ans;
   return 0;
}

这个代码之所以我没有加注释,是因为……

还可以做得更好\color{Purple}\text{还可以做得更好}

我们来看一眼代码:

  1. 输入a[i]
  2. 给b[i]赋值
  3. 给ans赋值

全程中 a 数组是没有意义的,也就是说,它可以被一个变量代替。

然后再来看一眼 b 数组 (b 数组:你不要过来啊)

全程我们只用到了 b[i] 元素和 b[i-1] 元素

是不是闻到了滚动数组的气息?

最终我们可以得出超级空间优化版\color{Goldenrod}\text{超级空间优化版}

#include<bits/stdc++.h>
using namespace std;
int n,a,b[2],i,ans=-2147483647;
// b数组用于存储序列和。
int main(){
   cin>>n;
   for(i=1;i<=n;i++){
       cin>>a;//将数组优化为一个变量
       if(i<2) b[1]=a;
       else b[i&1]=max(a,b[!(i&1)]+a);//前面推导的步骤,配合滚动数组食用
       ans=max(ans,b[i&1]);//更新答案
   }
   cout<<ans;
   return 0;
   /*
    *
    * 这里说一下代码里的位运算,&1 可以看做是 %2 。
    * 而加上感叹号就是 0变1 , 1变0 
    *
    */
}

最后的效果还是很明显的,从 2.13MB 变成了 820KB

戳这里可以看我的提交记录(已加入代码公开计划)

我也不知道最后写啥就让一个分割线结束这篇题解吧QAQ


update:修改了推导过程中一个bug,完善过程