题解 【洛谷P1115】最大子段和

这是一道枚举经典题。

本题有三种做法,各位需要根据每个题的数据范围来决定自己用哪种方法。

本题解中统一设最大和为Max。

方法一、 枚举子序列,从起点到终点求和。时间复杂度:O(n^3)

我们可以枚举它的子序列,也就是枚举它的长度、起点和终点。我们不妨设长度为i,起点为j,终点为k,当前子序列的和为s。于是我们就有了下列核心代码:


    Max=a[1];//最大和初始化为第一个数
    for(i=1;i<=n;i++)//枚举长度
        for(j=i;j<=n;j++){//枚举起点
            s=0;//当前序列和初始化为0
            for(k=i;k<=j;k++)//枚举终点
                s+=a[k];//计算和
            if(s>Max)Max=s;//比较
        }
    printf("%d",Max);//输出

这种方法在时限1s的题目中只能通过n<=450的范围。

但是本题的序列长度n<=200000。

所以采用该方法超时。

方法二、先求前缀和,再枚举。时间复杂度:O(n^2)

我们可以先求出从第一个数到当前数的和,然后枚举起点和终点,计算每一个子序列的和。于是我们就有了下列核心代码:


    memset(s,0,sizeof(s));//初始化前缀和
    for(i=1;i<=n;i++)s[i]=s[i-1]+a[i];//计算前缀和
    Max=a[i];//初始化最大值为第一个数
    for(i=1;i<=n;i++)
        for(j=i;j<=n;j++)
            Max=max(Max,s[j]-s[i-1]);//枚举起点和终点。
            //注意是i-1!因为计算从第i个数开始的和需要减去前i-1个数的和
    printf("%d",Max);

怎么有点像DP?

这种方法在时限1s的题目中只能勉强通过n<=10000的范围。

扫描二维码关注公众号,回复: 5259916 查看本文章

但是本题的序列长度n<=200000。

所以采用该方法也超时。

方法三、直接从头开始计算和,并每次记录最大和。时间复杂度:O(n)

不妨设s为以第i个数结尾的最大和,并每次计算和。

若当前和比最大值大,就更新最大值。

若当前和比0小,就把和清0。

核心代码:


    scanf("%d",&n);
    s=0;
    Max=-2e9;
    for(i=1;i<=n;i++){
        scanf("%d",&a);
        s+=a;
        if(s>Max)Max=s;
        if(s<0)s=0;
    }
    printf("%d",Max);

这种方法在时限1s的题目中能勉强通过n<=100000000的范围。

由于本题的序列长度n<=200000。

所以可以采用这种方法。

总结:一定要明白第三种方法的道理,并且能自己编写代码,这样水平才能有所提升。

猜你喜欢

转载自www.cnblogs.com/xsl19/p/10414741.html