单调队列学习笔记
前言
我们常说:“我又被单调队列了/kk”,意为“我比比自己小还比自己强的人吊打了”。
今天我们赖了解什么是“单调队列”。
分析
毫无疑问,最开始必定想到的是 \(\mathcal{O}(nk)\) 的暴力。
代码不贴啦,必定 \(\text{TLE}\)。
考虑优化。
要求的是每连续的 个数中的最大值,很明显,当一个数进入所要 "寻找" 最大值的范围中时,若这个数比其前面(先进队)的数要大,显然,前面的数会比这个数先出队且不再可能是最大值。
- 摘自 OI Wiki。
这是复杂度为 \(\mathcal{O}(n)\)。
对于样例分析
1 3 -1 -3 5 3 6 7
| 操作 | 队列 |
|---|---|
1 入队 |
{1} |
3 入队 |
{1,3} |
-1 入队,队内元素大于 -1 的出队 |
{-1} |
-3 入队,队内元素大于 -3 的出队 |
{-3} |
5 入队 |
{-3,5} |
3 入队,队内元素大于 3 的出队 |
{-3,3} |
6 入队,-3 不在窗内,出队。 |
{3,6} |
7 入队 |
{3,6,7} |
Code
- 摘自 OI Wiki。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxn 1000100
using namespace std;
int q[maxn], a[maxn];
int n, k;
void getmin() { //得到这个队列里的最小值,直接找到最后的就行了
int head = 0, tail = 0;
for (int i = 1; i < k; i++) {
while (head <= tail && a[q[tail]] >= a[i]) tail--;
q[++tail] = i;
}
for (int i = k; i <= n; i++) {
while (head <= tail && a[q[tail]] >= a[i]) tail--;
q[++tail] = i;
while (q[head] <= i - k) head++;
printf("%d ", a[q[head]]);
}
}
void getmax() { //和上面同理
int head = 0, tail = 0;
for (int i = 1; i < k; i++) {
while (head <= tail && a[q[tail]] <= a[i]) tail--;
q[++tail] = i;
}
for (int i = k; i <= n; i++) {
while (head <= tail && a[q[tail]] <= a[i]) tail--;
q[++tail] = i;
while (q[head] <= i - k) head++;
printf("%d ", a[q[head]]);
}
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
getmin();
printf("\n");
getmax();
printf("\n");
return 0;
}
//deque实现
#include <cstdio>
#include <queue> // 提供deque
#define MAXN 2000005
using namespace std;
struct Num{
int index,x;//需要记录单调队列内每个数的入队时间(index)和大小(x)
};
int a[MAXN]; //原数组
deque<Num> q; //单调队列
int main(void){
int n,m; //n表示序列长度,m表示滑动窗口长度
Num t;//保存当前元素
//输入
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
//问题解决
for (int i=1;i<=n;i++){
//先输出数a[i]前的最小值
if (q.empty()) //q空,即a[i]前没有元素
printf("0\n");
else { //否则判断队头是否需要出队并输出范围内的队头
if (q.front().index+m<i) //队头已经超出滑动窗口范围
q.pop_front(); // 弹出队头
printf("%d\n",q.front().x); //此时队一定非空(想想为什么)
}
while ((!q.empty()) && q.back().x>=a[i])
//当队列非空时,不断弹出队尾比当前元素大的元素
q.pop_back();
t.index=i;
t.x=a[i];
q.push_back(t);//将当前元素入队
//注意:当前元素无论如何都会入队(想想为什么)
}
return 0;
}
无耻求赞求关注(((q(≧▽≦q)

浙公网安备 33010602011771号