怎样求出区间和?
可以从l到r循环一遍,累加。
但这样的时间复杂度为O(r-l+1)。
如此高的时间复杂度显然是不能接受的。
我们采用前缀和进行优化。
什么是前缀和?
前缀和的第i项表示:从数列的第一项一直加到第i项的和。
用sum[]
表示前缀和数组,用x表示我们每一个输入的值。
显然sum[i]=sum[i-1]+x
举个栗子,对于以下数列:
原数列: 3,4,1,0,9
前缀和: 3,7,8,8,17
前缀和有什么用?
利用前缀和我们就可以在O(1)时间内处理出区间和。
对于L到R的区间和,前缀和数组为sum,原数组为a,则有:
sum[l-1]=a[1]+a[2]+...+a[l-1];
sum[r]=a[1]+a[2]+...+a[l-1]+a[l]+...+a[r];
sum[r]-sum[l-1]=a[1]-a[1]+a[2]-a[2]+...+a[l-1]-a[l-1]+a[l]+a[l+1]+...+a[r];
sum[r]-sum[l-1]=a[l]+...a[r];
那么sum[r]-sum[l-1]
即为我们要求的区间和。
可以感性理解一下。
怎么求出题目答案?
最容易想到的方法就是暴力枚举端点:
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
...
}
}
但显而易见,这绝对会超时。
我们采取双指针优化。
定义两个指针指向区间两个端点,初始时都为一。
只要当前区间和超过了m,就让左端点加一。
只要当前区间和大于上次区间和,就更新答案以及答案区间。
#include<cstdio>
using namespace std;
int n,m,x;
long long sum[4000005];
//前缀和数组
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
sum[i]=sum[i-1]+x;
//预处理前缀和
}
long long ans=-1;
//ans是最大值,所以一开始先赋值为-1,方便判断
int l=1,r=1,ansl,ansr;
//l,r为两个指针,开始都指向1
//ansl,ansr为最终答案
while(r<=n)
{
//防止越界
while(sum[r]-sum[l-1]>m&&l<r)
l++;
//sum[r]-sum[l-1]表示l到r的区间和
//大于m就无效,左端点加一
//要判断l<r因为防止越界
if(sum[r]-sum[l-1]>ans)
{
ans=sum[r]-sum[l-1];
ansl=l,ansr=r;
//更新答案
}
r++;
}
printf("%d %d %lld",ansl,ansr,ans);
return 0;
}