这道题其实就是求一个序列中的最大连续子序列的问题
方法一:前缀和
对于任意一个序列,我们采用前缀和的方式,这样可以快速求出一个区间的和,比如我们现在位于序列的第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;
}