P1115 最大子段和

题目描述
给出一个长度为 

n 的序列 

a,选出其中连续且非空的一段使得这段和最大。

输入格式
第一行是一个整数,表示序列的长度 

n。

第二行有 

n 个整数,第 

i 个整数表示序列的第 

i 个数字 



i

 。

输出格式
输出一行一个整数表示答案。

输入输出样例
输入 #1复制
7
2 -4 3 -1 2 -4 3
输出 #1复制
4
说明/提示
样例 1 解释
选取 
[
3
,
5
]
[3,5] 子段 
{
3
,

1
,
2
}
{3,−1,2},其和为 
4
4。

数据规模与约定
对于 
40
%
40% 的数据,保证 


2
×
1
0
3
n≤2×10 
3
 。
对于 
100
%
100% 的数据,保证 
1



2
×
1
0
5
1≤n≤2×10 
5
 ,

1
0
4




1
0
4
−10 
4
 ≤a 
i

 ≤10 
4
 。

大家不要把这题想得很复杂,能做出一个题首先要有做题的信念。这题的实际难度没有大家想得那么高。

直接枚举 

,

l,r,求所有区间的和然后取最大值的时间开销很大,不能通过本题,我们来思考更优秀的做法。

首先,我们现在纸上手算一下样例是怎么来的:

2 -4 3 -1 2 -4 3
ans:4
可以发现选 3 -1 2 是一种合法的方案。那么是怎么推出来的呢?

首先看到第一个数,是 
2
2。而 
2
2 后面是 
-4
-4,所以如果 
-4
-4 是答案的一部分,那么 
2
2 一定也要加上去(这样答案就增加了,会比原来优)。

随后是 
3
3。如果 
3
3 把前面的 
2
2 和 
-4
-4 加上去,结果是 
1
1。这个时候反而比原来的单独一个 
3
3 要小。所以如果答案含有 
3
3,就一定不会加上前面的 
2
2 和 
-4
-4(加上前面的部分答案变小,不如到这里为止)。

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

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

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

最后一个数 
3
3,如果将 
3
3 加上前面的序列,结果变成了 
3
3,没有变,所以这个可加可不加。

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

所以说了这么多,最终的结果是什么呢?

第一个数为一个有效序列
如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。
如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列
在执行上述处理的过程中实时更新当前有效序列的所有元素之和并取最大值。
然后就可能有人问了:考虑上面样例推导中,出现了一个可加可不加的 
3
3。如何处理?

结论是:对于可加可不加的数,不如加上。因为加上对答案没有坏处,而如果这个数后面还有一部分能让答案变多,因为本题求的子段是连续子段,不加上的话这两边就连不起来了。所以无脑加就行了。

最后取最大值即可。

#include
using namespace std;
int n,a[200020],b[200020],i,ans=-2147483647;

// b[i] 表示截止到 i 时,第 i 个数所在的有效序列的元素和。

int main(){
   cin>>n;
   for(i=1;i<=n;i++){
       cin>>a[i];
       if(i==1) b[i]=a[i];
       else b[i]=max(a[i],b[i-1]+a[i]);
       ans=max(ans,b[i]);
   }
   cout<    return 0;
}
然而对比这份代码的时空消耗,我们还可以做得更好。

我们来看一眼代码:

输入 



i

 。
用 



1

i−1

  和当前输入的 



i

  给 



i

  赋值。
用 



i

  给 



ans 更新答案。
首先发现全程中 

a 数组是没有意义的。我们每次只用到了当前使用的 



i

 。也就是说,它可以被一个变量代替。

其次考虑 

b 数组,每次对 



i

  更新只用到 



i

  和 



1

i−1

 。前者已经变成一个变量了,而后者,我们把 



1

i−1

  看成“上一个 



i

 ”,于是就相当于 



i

  是由上一个 



i

  和变量 

a 更新的。这也可以缩减成一个变量。

最终我们就得出了空间消耗大优化后的代码:

#include
using namespace std;
int n,a,b,i,ans=-2147483647;
int main(){
   cin>>n;
   for(i=1;i<=n;i++){
       cin>>a;
       if(i==1) b=a;
       else b=max(a,a+b);
       ans=max(ans,b);
   }
   cout<    return 0;
}
空间优化的效果还是很明显的,从 2.13MB 变成了 688KB。

你可能感兴趣的:(算法,数据结构)