题目链接:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=44
常见的做法有:枚举区间、动态规划等。
昨天突然想到了一个O(n)的算法,拿来分享给诸位
我们从后向前看这串数字 5 6 -2 3 -5 发现倒数第一位就是负数(-5),毫无疑问,这个负数肯定不在我的最大子串和里,因为我们舍去它并不会影响我们链接其它有价值的数,所以我们将它抛弃。
继续考察,这时我们遇到了3,我们考虑加不加入3。可能该有同学问了,3是正数,为啥子不直接加它还需要考虑?
因为我们想加入3就得加入链接它的数,链接它的数是负数(-2),所以肯定要考虑加入它值不值当,经过考察,我们发现即使加入了3和-2,它们总的来说还是有贡献的(1)那么我们就可以将它加入。
就像这样,我们每遇到一个元素都要考察加入它是否值当,也就是说,它与和它链接的子串和,如果是负数,我们将它舍去,如果是正数(代表有贡献)我们就可以将它加入到最大子串和里。
同时我们需要一个变量来记录最大子串和。
比如序列变成了 -5,-6,-2,3,-5 我们先舍去-5,考察到3的时候就已经得出来了最大子串,因此记录。
代码:
int sum = 0;
int max = 0;
for(int i=n-1; i>=0; i--)
{
sum += a[i];
if( sum < 0){
sum = 0;
}
if( sum > max)
max = sum;
}
记录这个区间的起始位置和终点位置也很简单,在max获得最大值的时候记录一下下标。然后从这个下标开始,sum和max清零,向后遍历,max获得最大值的时候再记录一边下标
#include<iostream>
using namespace std;
int main(){
int a[100];
int n; scanf("%d",&n);
for(int i=0; i<n; i++){
scanf("%d",&a[i]);
}
int sum = 0;
int max = 0;
int qd;
int zd;
for(int i=n-1; i>=0; i--)
{
sum += a[i];
if( sum < 0){
sum = 0;
}
if( sum > max) {
max = sum;
qd = i;
}
}
cout<<max;
max = 0;
sum = 0;
for(int i=qd; i<n; i++){
sum += a[i];
if( sum > max){
zd = i;
}
}
cout<<"起点:"<<qd<<"终点:"<<zd<<endl;
return 0;
}