PAT甲级1007 Maximum Subsequence Sum

4 篇文章 0 订阅
2 篇文章 0 订阅

这道题其实就是求一个序列中的最大连续子序列的问题

方法一:前缀和

对于任意一个序列,我们采用前缀和的方式,这样可以快速求出一个区间的和,比如我们现在位于序列的第k位,那么如何求以k结尾的最大连续子序列呢,其实就是使f[k]-f[i]最大即可,f是前缀和数组,然后i是第k位之前的某一位,要使f[k]-f[i]最大,f[k]是固定的,就是从第一个到第k位的和,那么我们就需要让f[i]最小,也就是说从前k-1位中找一个f[i]最小的,这样就可以求得以k位结尾最大的连续子序列

我们不必使用二重循环每次到k,都遍历一遍1到k-1寻找最小的前缀和f[i],只需要在循环中每次比较当前的f[i]与之前最小的作比较即可,这样就可以省去一重循环,然后如果f[k]-f[i]比ans更大,那么更新ans,同时记录答案,我们用anss作为答案的起始点,anse作为答案的结束点,记住anss要+1,因为我们减去的那一位是不要的,也就是选择的那一位的后一位

记住更新到k最小的前缀和要放到后面,如果放到前面,那很有可能会出现自身减自身这种情况,那么得出的答案就是0,其实如果是一个负数的话,我们必须要选一个那么不会是0,其实意思就是我们必须选一个,如果全是负数,那我们最大的答案就是负值,然后自身减自身是0,这不是答案,如果放到循环的后面,那么我们每次循环到第k位,得到的都是前k-1位中最小的那个

记住如果全是负值,我们需要输出0,并且输出序列首位和尾位的值特判一下

#include<cstdio>
using namespace std;
int a[10010];
long long f[10010];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        f[i]=f[i-1]+a[i];
    }
    long long ans=-1e9;
    int minx=0,anss,anse;
    for(int i=1;i<=n;i++) {
        if(f[i]-f[minx]>ans) {
            ans=f[i]-f[minx];
            anss=minx+1;
            anse=i;
        }
        if(f[i]<f[minx]) minx=i;
    }
    if(ans<0) {
        printf("0 %d %d\n",a[1],a[n]);
        return 0;
    }
    printf("%lld %d %d\n",ans,a[anss],a[anse]);
    return 0;
}

方法二:模拟

我们用temp变量记录当前的最大连续子序列的和,如果加上当前这一位小于0,那么直接把temp置为0,把temps置为这一位的后一位,因为要求最大,不加这一段还好,加了更少,那么我们当然选择不加,如果temp大于ans,那么更新答案,把序列的首位记为anss,末位记为anse,最后如果ans<0,表明序列全为负值,直接把ans记为0即可,anss初始记为1,anse初始记为n

#include<cstdio>
using namespace std;
int a[10010];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
    }
    long long ans=-1,temp=0;
    int anss=1,anse=n,temps=1;
    for(int i=1;i<=n;i++) {
        temp+=a[i];
        if(temp<0) {
            temp=0;
            temps=i+1;
        }
        else if(temp>ans) {
            ans=temp;
            anss=temps;
            anse=i;
        }
    }
    if(ans<0) {
        ans=0;
    }
    printf("%lld %d %d\n",ans,a[anss],a[anse]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值