单调队列
P1886 滑动窗口 /【模板】单调队列
「单调」指的是元素的「规律」——递增(或递减)。
「队列」指的是元素只能从队头和队尾进行操作。
要求的是每连续的 k k k 个数中的最大(最小)值,很明显,当一个数进入所要 “寻找” 最大值的范围中时,若这个数比其前面(先进队)的数要大,显然,前面的数会比这个数先出队且不再可能是最大值。
也就是说——当满足以上条件时,可将前面的数 “弹出”,再将该数真正 p u s h push push 进队尾。
这就相当于维护了一个递减的队列,符合单调队列的定义,减少了重复的比较次数,不仅如此,由于维护出的队伍是查询范围内的且是递减的,队头必定是该查询区域内的最大值,因此输出时只需输出队头即可。
显而易见的是,在这样的算法中,每个数只要进队与出队各一次,因此时间复杂度被降到了 O ( N ) O(N) O(N)。
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
const int maxn = 1e6 + 5;
int n, m;
int a[maxn];
deque<int> q;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
{
while (!q.empty() && a[q.back()] > a[i])
q.pop_back();
q.push_back(i);
if (i >= m)
{
while (!q.empty() && q.front() <= i - m)
q.pop_front();
printf("%d ", a[q.front()]);
}
}
printf("\n");
while (!q.empty())
q.pop_back();
for (int i = 1; i <= n; i++)
{
while (!q.empty() && a[q.back()] < a[i])
q.pop_back();
q.push_back(i);
if(i>=m)
{
while(!q.empty()&&q.front()<=i-m) q.pop_front();
printf("%d ",a[q.front()]);
}
}
return 0;
}
单调队列应用场景:
单调队列(Monotonic Queue)是一种数据结构,常用于解决一些与滑动窗口相关的问题。它可以在 O ( 1 ) O(1) O(1) 的时间复杂度内实现以下操作:
- 在队尾添加元素
- 在队头移除元素
- 获取当前队列中的最大(或最小)元素
使用单调队列的典型场景包括:
-
滑动窗口最大值(或最小值)问题:给定一个数组和窗口的大小,需要找到每个窗口内的最大(或最小)值。单调队列可以帮助我们在窗口滑动的过程中高效地获取最大(或最小)值。
-
求解滑动窗口的某种性质:有时候我们需要在滑动窗口中求解的不仅仅是最大(或最小)值,还可能是其他一些特定的性质,比如窗口内的元素之和、平均值等。通过维护一个适当的单调队列,我们可以在滑动窗口中高效地求解这些性质。
-
解决一些需要在固定大小的窗口中维护最大(或最小)值的问题:比如在一个数据流中,需要不断更新当前窗口的最大(或最小)值。单调队列可以帮助我们在数据流中高效地维护这些值。
P1440 求m区间内的最小值
单调队列板子题,使用单调队列时需注意边界处理情况。手玩一遍即可。
P1714 切蛋糕
P10893 城市化发展委员会
Pending... \color{grey}\text{Pending...} Pending...
P10878 [JRKSJ R9] 在相思树下 III
Pending... \color{grey}\text{Pending...} Pending...