nyoj 44 子串和【最大子串和】

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/qq_39652552/article/details/82109633
44