单调队列学习笔记

单调队列学习笔记

定义
一个有规律的队列.(满足单调递增或单调递减.)
ps单调队列不会单独出题.主要用来优化.(Dp)
54tg luogu1886
拿这道题为例,要求出min_num,max_num.
因为他的序列固定,我们一格格往后移动就好,我们会想到每次O(n)扫一遍区间.找出min_num,
max_num.这样的复杂度是O(n * m) 的,这是我们所不能承受的.
所以我们要用到单调队列.

写在前面的:
单调队列的一些性质:
1、维护区间最值;
2、去除冗杂状态;
3、保持队列单调(最大值是单调递减序列,最小值是单调递增序列);
4、最优选择在队首.

代码有详解.(自为风月马前卒's code)

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=10000001;
int read(int & n)
{
    char c='.';int x=0,flag=0;
    while(c<'0'||c>'9')
    {
        c=getchar();
        if(c=='-')flag=1;
    }
    while(c>='0'&&c<='9')
    {
        x=x*10+(c-48);
        c=getchar();
    }
    if(flag==1)n=-x;
    else n=x;
}
int n,m;
int a[MAXN];
int q[MAXN],p[MAXN],h=0,t=0;//q是单调队列,p是编号 
void find_min() 
{
    h=1;t=0; 
    for(int i=1;i<=n;++i)// 
    {
        
        while(h<=t&&q[t]>=a[i]) //如果i < j ,且a[i] >= a[j] ,那么我们选择后者更优 
            t--;
        q[++t]=a[i]; 
        p[t]=i;
        while(p[h]<=i-m)//如果第一个的编号该出队了,排空 
            h++;
        if(i>=m)//如果这个序列足够m长,每次出这个队列的第一个元素(第一个于) 
            printf("%d ",q[h]);// 
    }
    printf("\n");
} 
void find_max() 
{
    h=1;t=0;
    memset(q,0,sizeof(q));
    memset(p,0,sizeof(p));
    for(int i=1;i<=n;++i)
    {
        while(h<=t&&q[t]<=a[i])
            t--;
        q[++t]=a[i];
        p[t]=i;
        while(p[h]<=i-m)
            h++;
        if(i>=m)
            printf("%d ",q[h]);
    }
    printf("\n");
}
int main()
{
    read(n);read(m);
    for(int i=1;i<=n;i++)
        read(a[i]);
    find_min();
    find_max();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/tpgzy/p/9291542.html