初步学习单调队列,
先从简单点的开始理解。
每次找出在(i-m,i)范围内的最小sum[j]值。
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
ll sum[maxn];
list<int> lt;
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
lt.clear();
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%lld",&sum[i]);
sum[i]=sum[i-1]+sum[i];
}
ll maxs=sum[1];
lt.push_front(1);
for(int i=2;i<=n;i++){
while(!lt.empty()&&i-lt.back()>m){
lt.pop_back();
}
maxs=max(maxs,sum[i]-sum[lt.back()]);
while(!lt.empty()&&sum[i]<sum[lt.front()]){
lt.pop_front();
}
lt.push_front(i);
}
printf("%lld\n",maxs);
}
return 0;
}
初始化先用sum数组存储前缀和,
sum[i]-sum[j]这样就可以求出区间和了。
然后在第二个for循环里更新
因为题目要求是在m的范围内
所以每次进来先检测是否 i - 队列的末尾>m,
while(!lt.empty()&&i-lt.back()>m){
lt.pop_back();
}
(为什么不是等于,因为前缀和数组的被减数是包含前面的)
之后更新最大值maxs。
maxs=max(maxs,sum[i]-sum[lt.back()]);
为了保证队列是从大到小,并且每次要求sum[j]尽可能小,所以,
while(!lt.empty()&&sum[i]<sum[lt.front()]){
lt.pop_front();
}
如果队首的元素的sum比当前的sum[ j ] 大,那就pop掉,
因为当前的 i 又新它的sum又小 ,
最后
lt.push_front(i);
将i插入队首就好