一、题干:acwing单调队列
二、思路
按照惯例,先想暴力做法。每移动一次窗口遍历一次窗口内元素,记录最值,时间复杂度O(nk)。
那么该怎么优化呢,遍历n次窗口肯定没办法减少,那就只能从搜索窗口内最值元素的速度入手了。
很自然的就能想到,如果我们让窗口内的元素排好序,那么搜索的时候只要输出端点值就好了。可这还有几个问题,窗口内的元素会变化,总不能变化一次排序一次吧,那也太花时间了。
能不能让窗口内的元素变化的时候就自动排序好呢?
欸!单调队列恰好能解决这个问题。我们每一次变化的时候,先把不在窗口内的那个元素删掉,然后加入新元素的时候我们动一些小手段,我们把队列内比新元素大的全部丢掉,这样子整个队列从左到右就保持了递增的单调性,左端点一定最小且在窗口内。我知道你可能担心什么,万一我们把之后的最值丢掉了怎么办?不会的,因为比新元素大的值会比新元素更早出队,他们没有机会从新元素手里拿走最小值的称号了。输出最大值的时候我们只需要把这个过程对称着再做一遍就好了。
三、代码实现
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N],q[N];
int main()
{
int n,k;
cin>>n>>k;
for(int i = 0; i < n; i ++ ) cin>>a[i];
int hh = 0, tt = -1;
for(int i = 0; i < n; i ++ )
{
if(hh <= tt && q[hh] < i - k + 1) hh ++;
while(hh <= tt && a[i] <= a[q[tt]]) tt --;
q[++ tt] = i;
if(i >= k - 1) cout << a[q[hh]]<<' ';
}
cout<<'\n';
hh = 0, tt = -1;
for(int i = 0; i < n; i ++ )
{
if(hh <= tt && q[hh] < i - k + 1) hh ++;
while(hh <= tt && a[i] >= a[q[tt]]) tt --;
q[++ tt] = i;
if(i >= k - 1) cout << a[q[hh]]<<' ';
}
return 0;
}