单调队列洛谷模板题

P1886 滑动窗口 /【模板】单调队列

 1 //单调队列
 2 //最小值为例:
 3 //方框从左往右勾选 
 4 //一个数出现时,其左边元素已经被判断过是否为最小值
 5 //若这个数比左边元素小,则其为更优解(数值小且不会先超出方框
 6 //则其左边元素有生之年已经没有用了,直接删掉(既生瑜何生亮
 7 //若这个数比左边元素大,则直接入队(数值大但后出框
 8 //这个数可能在左边元素出框后成为最小值,因此入队候选(熬死上一任 
 9 //最后再删掉队中所有出框元素 
10 //因此在此队列中,数组中的最小值会让其左边的数全部出队
11 //即队首元素始终为最小值,后方元素为候选项且队列严格递增 
12 //求最小值输出队首元素即可(方框长度不够时需要跳过 
13 //最大值同理,不多赘述 
14  
15 #include <bits/stdc++.h>
16 using namespace std;
17 int n,k;
18 const int maxn=1e6+5;
19 struct node{
20     int w,vai;
21 };
22 //w记录下标,方便判断是否超出窗口
23 //vai记录数值, 方便比较和输出 
24 node minq[maxn],maxq[maxn];
25 //最大值和最小值的单调队列 
26 int minl,minr,maxl,maxr;
27 int a[maxn];
28 int main(){
29     scanf("%d%d",&n,&k);
30     maxl=minl=1;
31     minr=maxr=0;
32     //l严格指向队首,r严格指向队尾
33     //即l<=r时,队列不为空 
34     for(int i=1;i<=n;i++){
35         scanf("%d",&a[i]);
36     }
37     for(int i=1;i<=n;i++){
38         //求最小值 
39         while(minl<=minr&&minq[minr].vai>=a[i])minr--;
40         //队列不为空时,a[i]较小,为更优解 
41         //a[i]之前的元素有生之年与a[i]同框无法作为最小值,去掉
42         //队尾元素在a[i]之前做过最小值或其候选项,现在已经没用了 
43         minq[++minr].vai=a[i];
44         minq[minr].w=i;
45         while(minq[minl].w<=i-k)minl++;
46         //如果队首元素(数组左边)超出方框则去掉 
47         if(i>=k)printf("%d ",minq[minl].vai);
48         //特判,如果方框长度不够则不输出
49         //i指方框末端位置 
50     }
51     printf("\n");
52     for(int i=1;i<=n;i++){
53         //求最大值 
54         while(maxl<=maxr&&maxq[maxr].vai<=a[i]){
55             maxr--;
56         }
57         maxq[++maxr].vai=a[i];
58         maxq[maxr].w=i;
59         while(maxq[maxl].w<=i-k)maxl++;
60         if(i>=k)printf("%d ",maxq[maxl].vai);
61     }
62     printf("\n");
63     return 0;
64 } 

P2032 扫描

这题更弱,上面代码改成求最大值再改一下数据范围就能AC

 

P1714 切蛋糕

这题是单调队列加前缀和,long long开得没必要,可以换成int

 1 //单调队列加前缀和
 2 //求长度不大于m区间最大值就是求
 3 //下标相差不大于m+1的前缀和的差的最大值
 4 //求a[i]+...+a[i+m]=s[i+m]-s[i-1],相差m+1
 5 //固定方框后方不懂,前方前缀和越小,差越大
 6 //即求前缀和的单调上升队列 
 7 #include <bits/stdc++.h>
 8 using namespace std;
 9 int n,m;
10 const int maxn=500005;
11 int lucky[maxn];
12 long long s[maxn];
13 struct node{
14     long long vai;
15     int w;
16 }que[maxn];
17 int l=1,r;
18 long long ans=-0x7f7f7f7f;
19 int main(){
20     scanf("%d%d",&n,&m);
21     for(int i=1;i<=n;i++){
22         scanf("%d",&lucky[i]);
23         s[i]=s[i-1]+lucky[i];
24     }
25     for(int i=1;i<=n;i++){
26         while(l<=r&&que[l].w<i-m)l++;
27         //为什么加了等号是错的 
28         //因为在前缀和中,a[i]到a[i+m]的区间和表示为s[i+m]-s[i-1]
29         //因此在出队时不能加等号,保留区间左端前一个数的前缀和
30         //所得得数才是此区间的和,不然就变成了
31         //区间【i+1~i+m】的和 
32         ans=max(ans,s[i]-que[l].vai);
33         while(l<=r&&s[i]<=que[r].vai)r--; 
34         que[++r].vai=s[i];
35         que[r].w=i;
36     }
37     printf("%d",ans);
38     return 0;
39 } 

 

posted @ 2022-09-17 10:59  九州霜  阅读(56)  评论(0编辑  收藏  举报