描述
输入一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6
输入格式
第一行两个数n,m(n,m<=300000)
第二行有n个数,要求在n个数找到最大子序和
输出格式
一个数,数出他们的最大子序和
样例输入
6 4 1 -3 5 1 -2 3
样例输出
7
这题是单调队列的典型题。首先可以把区间和问题转换为两个前缀和相减的问题。朴素算法就是枚举左右端点,但算法复杂度为O(n^2),这道题显然是过不了的。
所以我们可以只枚举右端点i,当i固定时,找一个左端点j,其中j属于[i-m,i-1]并且sum[j]最小。
不妨比较一下任意两个位置j和k,如果k<j<i并且是sum[k]>=sum[j],那么对所以大于等于i的右端点来说,k都不会是一个好选择,直接移除队列比较好。
代码如下
#include<bits/stdc++.h> using namespace std; int a[300005],q[300005],sum[300005]; int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) { sum[i]=sum[i-1]+a[i]; } int l=1,r=1,ans=-99999999; q[1]=0; for(int i=1;i<=n;i++) { while(l<=r&&q[l]<i-m) l++; ans=max(ans,sum[i]-sum[q[l]]); while(l<=r&&sum[q[r]]>=sum[i]) r--; q[++r]=i; } cout<<ans<<endl; return 0; }