标签:ons int typedef 限制 问题分析 整数 位置 双端队列 std
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](
标签:ons int typedef 限制 问题分析 整数 位置 双端队列 std
原文地址:https://www.cnblogs.com/hbhszxyb/p/13130924.html