单调队列的简单运用

洛谷 P1440 求m区间内的最小值
洛谷 P1886 滑动窗口
洛谷P1714 切蛋糕

求m区间内的最小值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int n,k,q[2005000],p[2005000],head=1,tail=0,x;
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
} 
int main(){
    n=read();k=read();
    for(register int i=1;i<=n;++i) q[i]=read();
    for(register int i=1;i<=n;++i) {
        printf("%d\n",q[p[head]]);//每次进来一个数便输出一个数
        if(i-p[head]+1>k&&head<=tail) ++head;//如果当前队列中的区间长度大于k,说明队首的元素要出队了
        while(q[p[tail]]>q[i]&&head<=tail) --tail;//判断,如果当前读取到的元素小于队尾元素,那么需要更新值,因为在这个区间中出现了更小的数,而队列又要保证单调性,所以队尾的元素要出队。
        p[++tail]=i;
    }
    return 0;
}

滑动窗口

//这道题跟上面那道题差不多,只不过在一开始的时候我们预处理一下。再一个就是这道题还要输出区间最大值。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int n,k,head=1,tail=0,a[1000050],q[1000050];
inline int read(){
    int x=0,w=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
    return x*w;
} 
int main(){
    n=read();k=read();
    for(register int i=1;i<=n;++i) a[i]=read();
    //下面的操作是输出区间内的最小值
    for(register int i=1;i<=k;++i) {
        while(head<=tail&&a[q[tail]]>a[i]) --tail;
        q[++tail]=i;
    }//这里就是上面所讲到的预处理,我们得保证先读入k个数,既能符合题目要求,又方便后面的操作
    for(register int i=k+1;i<=n;++i) {
        printf("%d ",a[q[head]]);
        if(i-q[head]+1>k) ++head;
        while(head<=tail&&a[q[tail]]>a[i]) --tail;
        q[++tail]=i;
    }   
    printf("%d\n",a[q[head]]);
    head=1;tail=0;
    //下面的操作是输出k区间内的最大值
    for(register int i=1;i<=k;++i) {
        while(head<=tail&&a[q[tail]]<a[i]) --tail;
        q[++tail]=i;
    }
    for(register int i=k+1;i<=n;++i) {
        printf("%d ",a[q[head]]);
        if(i-q[head]+1>k) ++head;
        while(head<=tail&&a[q[tail]]<a[i]) --tail;
        q[++tail]=i;
    }   
    printf("%d\n",a[q[head]]);
    return 0;
}

切蛋糕

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int x,n,q[500500],head=1,tail=0,sum[500500],k,ans;
int main(){
    scanf("%d%d",&n,&k);
    for(register int i=1;i<=n;++i){
        scanf("%d",&x);sum[i]=sum[i-1]+x;
        while(i-q[head]>k&&head<=tail) ++head;
        //注意这里的i-q[head]不需要在后面+1, 
        //相当于我们要求i到j之间的和,因为sum表示前缀和, 
        //所以是sum【j】-sum【i-1】而不是sum【j】-sum【i】,
        //所以这里不用加1,其他的和裸的单调队列的简单处理都一样。
        while(head<=tail&&sum[q[tail]]>sum[i]) --tail;
        q[++tail]=i;
        ans=max(ans,sum[i]-sum[q[head]]);
    }
    printf("%d",ans);
    return 0;
}

小结:其实在这之前,一直对单调队列有所恐惧,我也不知道为什么,总是想不对,讲的时候也总是听不太懂,写的时候也不知道如何实现,不过今天总算是入门了,以后还得多加练习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值