单调队列学习笔记
定义
一个有规律的队列.(满足单调递增或单调递减.)
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;
}