单调队列
引入
在学习单调队列前,让我们先来看一道例题。
最暴力的想法很简单,对于每一段
很显然,这其中进行了大量重复工作,除了开头
这时所用到的就是单调队列了。
定义
顾名思义,单调队列的重点分为「单调」和「队列」。
「单调」指的是元素的「规律」——递增(或递减)。
「队列」指的是元素只能从队头和队尾进行操作。
Ps. 单调队列中的 "队列" 与正常的队列有一定的区别,稍后会提到
例题分析
解释
有了上面「单调队列」的概念,很容易想到用单调队列进行优化。
要求的是每连续的
也就是说——当满足以上条件时,可将前面的数 "弹出",再将该数真正 push 进队尾。
这就相当于维护了一个递减的队列,符合单调队列的定义,减少了重复的比较次数,不仅如此,由于维护出的队伍是查询范围内的且是递减的,队头必定是该查询区域内的最大值,因此输出时只需输出队头即可。
显而易见的是,在这样的算法中,每个数只要进队与出队各一次,因此时间复杂度被降到了
而由于查询区间长度是固定的,超出查询空间的值再大也不能输出,因此还需要 site 数组记录第
过程
例如我们构造一个单调递增的队列会如下:
原序列为:
因为我们始终要维护队列保证其 递增 的特点,所以会有如下的事情发生:
操作 | 队列状态 |
---|---|
1 入队 | {1} |
3 比 1 大,3 入队 | {1 3} |
-1 比队列中所有元素小,所以清空队列 -1 入队 | {-1} |
-3 比队列中所有元素小,所以清空队列 -3 入队 | {-3} |
5 比 -3 大,直接入队 | {-3 5} |
3 比 5 小,5 出队,3 入队 | {-3 3} |
-3 已经在窗体外,所以 -3 出队;6 比 3 大,6 入队 | {3 6} |
7 比 6 大,7 入队 | {3 6 7} |
例题参考代码
#include <cstdlib>
#include <cstring>
#include <iostream>
constexpr int MAXN = 1000100;
using namespace std;
int q[MAXN], a[MAXN];
int n, k;
void getmin() { // 得到这个队列里的最小值,直接找到最后的就行了
int head = 0, tail = -1;
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++;
cout << a[q[head]] << ' ';
}
}
void getmax() { // 和上面同理
int head = 0, tail = -1;
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++;
cout << a[q[head]] << ' ';
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
getmin();
cout << '\n';
getmax();
cout << '\n';
return 0;
}
Ps. 此处的 "队列" 跟普通队列的一大不同就在于可以从队尾进行操作,STL 中有类似的数据结构 deque。
例题 2 Luogu P2698 Flowerpot S
给出
将所有水滴按照
我们依然可以使用一个递增,一个递减两个单调队列在
参考代码
#include <algorithm>
#include <iostream>
using namespace std;
constexpr int N = 100005;
using ll = long long;
int mxq[N], mnq[N];
int D, ans, n, hx, rx, hn, rn;
struct la {
int x, y;
bool operator<(const la &y) const { return x < y.x; }
} a[N];
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> D;
for (int i = 1; i <= n; ++i) cin >> a[i].x >> a[i].y;
sort(a + 1, a + n + 1);
hx = hn = 1;
ans = 2e9;
int L = 1;
for (int i = 1; i <= n; ++i) {
while (hx <= rx && a[mxq[rx]].y < a[i].y) rx--;
mxq[++rx] = i;
while (hn <= rn && a[mnq[rn]].y > a[i].y) rn--;
mnq[++rn] = i;
while (L <= i && a[mxq[hx]].y - a[mnq[hn]].y >= D) {
ans = min(ans, a[i].x - a[L].x);
L++;
while (hx <= rx && mxq[hx] < L) hx++;
while (hn <= rn && mnq[hn] < L) hn++;
}
}
if (ans < 2e9)
cout << ans << '\n';
else
cout << "-1\n";
return 0;
}
创建日期: 2018年7月11日