滑动窗口问题(单调队列)

Posted 偶尔爆零的蒟蒻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑动窗口问题(单调队列)相关的知识,希望对你有一定的参考价值。

An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position Minimum value Maximum value
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7
Your task is to determine the maximum and minimum values in the sliding window at each position.

Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.

Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7

题意

给定长度为 n n n的数组,和一个长度为 k k k的滑动窗口,开始时窗口在最左端,结束时在最右端,每次移动一个位置,问每次窗口中的最小、最大元素是什么


先考虑最大 每个元素入队时,进行判断,使得队列从头到尾,单调递减,队头是当前窗口内最大值。 从队尾插入元素时,判断该入队元素是否比队尾元素小,如果是,则保持单调递减可入队,否则,应当找一个合适的位置,即队尾元素出队若干个,使得该入队元素小于此时队尾元素。当入队元素比队头大时,队空,再入此最大元素。
还需判断窗口内元素个数,如果大于窗口容量,则队头出队,因为该队头是上一次移动时的最值,窗口滑走了,已经不包括该元素了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
#define ff first
#define ss second
#define ms(a,x) memset(a,x,sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define inf 0x3f3f3f3f  //10 6110 9567<2^30=10 7374 1824
#define iinf 0x7fffffff
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=9901;
const int MAX=1e6;
/*
头   ... 尾
head ... top
1/队内容量大于k,去掉队头
2/队尾插入时,要保持单调,否则队尾出若干元素
*/
int n,k;
int a[MAX+5];
pii qi[MAX+5],qa[MAX+5];//最小值队,最大值队
int ansi[MAX+5],ansa[MAX+5];
int main()
    cin>>n>>k;
    //最大值队队头,最大值队队尾
    int ta=1,wa=0,ti=1,wi=0;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=k-1;i++)
        //max
        //非空&&入队元素比队尾大  找入队位置保持单调递减 队头最大
        while(wa>=ta&&qa[wa].ff<=a[i])wa--;
        qa[++wa].ff=a[i];
        qa[wa].ss=i;
        
        //min   找入队位置保持单调递增 队头最小
        while(wi>=ti&&qi[wi].ff>=a[i])wi--;
        qi[++wi].ff=a[i];
        qi[wi].ss=i;
    
    //每次的答案
    for(int i=k;i<=n;i++)
        //max
        //同样
        while(wa>=ta&&qa[wa].ff<=a[i])wa--;
        qa[++wa].ff=a[i];
        qa[wa].ss=i;
        //窗口容量如果大于k,则去掉之前的队头
        while(qa[wa].ss-qa[ta].ss+1>k)ta++;
        ansa[i]=qa[ta].ff;
        
        //min
        while(wi>=ti&&qi[wi].ff>=a[i])wi--;
        qi[++wi].ff=a[i];
        qi[wi].ss=i;
        
        while(qi[wi].ss-qi[ti].ss+1>k)ti++;
        ansi[i]=qi[ti].ff;
    
    for(int i=k;i<=n;i++)
        cout<<ansi[i]<<" ";
    cout<<endl;
    for(int i=k;i<=n;i++)
        cout<<ansa[i]<<" ";
    cout<<endl;


以上是关于滑动窗口问题(单调队列)的主要内容,如果未能解决你的问题,请参考以下文章

P1886 滑动窗口

数据结构——单调栈&单调队列(解决滑动窗口问题)

滑动窗口问题(单调队列)

滑动窗口问题(单调队列)

滑动窗口问题(单调队列)

滑动窗口/模板单调队列 题解