最大子段和之带长度限制
带长度限制的最大子段和
题目模型
- 一个整数序列\(a_1,a_2,……,a_n\) ,求最大的长度不超过
K
的子段的数值和。
问题分析
-
求以
a[i]
结尾的最大子段和,我们需要维护一个最小的前缀sum[j]
,即[j+1,i]
为所求。 -
但要求子段和区间长度不能大于
K
,则需要满足:i-j<=k
。 -
如果
j'>j
且sum[j']<sum[j]
,显然sum[j]
对后面的求解就没有用了,所以我们可以用一个单调队列维护最远不超过K
的最小前缀和。 -
Code
#include <bits/stdc++.h> const int maxn = 1e5+5,Inf=0x3f3f3f3f; typedef long long LL; int a[maxn<<1],sum[maxn<<1]; void Solve(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i];//前缀和 } int ans=-Inf,l,r;//l:记录答案左边界,r:记录右边界 std::deque<int> q;//双端队列维护的 for(int i=1;i<=n;++i){ //因为区间[l,r]和为sum[r]-sum[l-1]所以要维护最小的sum[l-1] while(!q.empty() && sum[i-1]<sum[q.back()]) q.pop_back(); //保证最远的左端点离i的距离不能超过k while(!q.empty() && i-q.front()>k) q.pop_front(); q.push_back(i-1);//当前队列要么为空,要么队尾前缀和小于su[i-1] if(sum[i]-sum[q.front()]>ans){ ans=sum[i]-sum[q.front()]; l=q.front()+1;//注意左边界要+1 r=i; } } printf("%d %d %d\n",ans,l,r); } int main(){ Solve(); return 0; }
-
Code
手摸双端队列版,建议大家手写队列#include <bits/stdc++.h> const int maxn = 1e5+5,Inf=0x3f3f3f3f; typedef long long LL; int a[maxn<<1],sum[maxn<<1],q[maxn<<1]; void Solve(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } int ans=-Inf,l,r; int head=0,tail=0; for(int i=1;i<=n;++i){ while(head<tail && sum[i-1]<sum[q[tail-1]]) tail--; while(head<tail && i-q[head]>k) head++; q[tail++]=i-1;//tail指向队尾的后一个位置 if(sum[i]-sum[q[head]]>ans){ ans=sum[i]-sum[q[head]]; l=q[head]+1; r=i; } } printf("%d %d %d\n",ans,l,r); } int main(){ Solve(); return 0; }
-
习题:[HDU-3415](
hzoi