Description
给出一个长度为n(1<=n<=100000)的整数序列,选择长度不超过k(1<=k<=n)的段,使得总和最大。
Solution
方法一:枚举左端点l和右端点r,然后扫一遍计算总和并选取最大值。时间复杂度O(n^3)
方法二:预处理sum[i]表示前i个数的和,枚举左端点l和右端点r,然后O(1)计算总和并选取最大值。时间复杂度O(n^2)
方法三:仍然是预处理sum[i]表示前i个数的和,只枚举右端点r,问题就转换成了在sum[r-k..r-1]中找一个最小值。那么我们维护一个元素单调递增的队列,每次处理完一个r后把队尾所有不小于sum[r]的元素删掉后把sum[r]放进队尾。那么每次只用把队头中元素在原数组中的下标
#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,q[100002],h,t=1;
long long s[100001],ans=-0x7fffffff;
int main()
{
q[1]=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
s[i]=s[i-1]+x;
while(h<=t&&s[q[t]]>=s[i]) t--;
q[++t]=i;
while(q[h]<i-k) h++;
ans=max(ans,s[i]-s[q[h]]);
}
printf("%lld",ans);
return 0;
}