【OI】单调队列

随声附和 提交于 2020-02-09 09:06:08

所谓单调队列,就是一个保持着某种性质的队列,通常是队列从队头到队尾,维护一种递增递减的关系。

这种队列通常用来解决一些连续区间的最值问题。

这种队列的入队要保证符合当前的性质,例如一个递增的单调序列(从左到右是从头到尾):7,9,10,11

这时,入队时要保证是递增的:例如12这个元素可以入队,而10这个元素不行。

这时我们看队头,这就是整个队列中最小的元素。所以,递增的队列可以维护最小值;递减的队列可以维护最大值。

当然,实现一个单调队列需要看题目要求,通常需要对队头和队尾进行更新。

 

例如这道题:

 

P1886 滑动窗口

题目描述

现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

 

这道题可以使用单调队列来做。

在滑动窗口的时候,改变只有窗口序列中的第一个元素和最后一个元素,所以我们可以把一开始的序列同样的放到两个单调队列中,一个递增,一个递减,分别维护最小值和最大值。

用求最小值(递增单调队列)做例子,我们对每个元素运行这样一个判断:

1.如果当前元素<队尾,那么删除队尾。不停执行该操作,直到队为空且没有可以删除的队尾为止。

2.在队尾加上当前元素。

3.如果队头离开了窗口的范围,那么删除队头。

求最大值的操作类似。

 

根据上题,我们可以总结出单调队列一般有这样的步骤:

1.如果当前元素小于或者大于(递增或者递减)队尾,那么删除队尾。不停执行该操作,直到队为空且没有可以删除的队尾为止。

2.在队尾加上当前元素。

3.如果队头离开了题目的范围,那么删除队头。

 

P1440 求m区间内的最小值

题目描述

一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。

 

这道题依然用单调队列做。注意,这道题的区间指的是每一项的前m个数,不包括这一项。

所以,我们维护一个递增的单调队列,长度最长为m,步骤类似上文所讲。

代码:

#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
#include<map>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const ll INF=99999999;
const int MaxN = 2000010;
int a[MaxN];
int que[MaxN][2];
int n,m,head = 1,tail;
int main()
{
    //freopen("testdata.in","r",stdin);
    //freopen("testdata.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    
    
    
    
    for(int i = 1; i <= n; i++){
        scanf("%d",&a[i]);    
    } 
    printf("0\n");
    que[++tail][0] = a[1];
    que[tail][1] = 1;
    for(int i = 2; i <= n; i++){
        
        
        
        while(head <= tail && a[i-1] < que[tail][0]){
            tail--;
        }
        que[++tail][0] = a[i-1];
        que[tail][1] = i-1;
    
        while(head <= tail &&que[head][1] < i-m)
            head++;
        
        
        printf("%d\n",que[head][0]);
    }
    
    return 0;
}
P1440

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!