单调队列 poj2823 Sliding Window

传送门:点击打开链接

题意:有n个数字,有个宽度为k的窗口,从最左边向右边移动,每次都框住k个数字,依次输出这些框中的最大值和最小值

思路:运用单调队列维护。一般deque我们都手动模拟,因为一般单调对列对时间复杂度要求都会比较高。

一般令cur=rear=0,rear表示尾指针,其实这个指针是取不到的,也就是说左开由闭。当cur<rear则表示队列中存在元素

单调队列数字都是从队尾进,要求最值的时候一般都是在堆首取。所以说,队首要求的是最大值还是最小值,也决定了整个队列是递增还是递减。

单调对列一般都是这样考虑问题的,假如我现在是要维护求最小值的单调队列

数字一般从队尾插入t,先要从队尾开始,将大于t的数字全部删除,然后才把t加入到队尾

这时,再考虑队首开始的是否都符合题意范围,把不符合的也删除,也就是cur++移动首指针。

然后再取队首元素,此时就是当前的最小值

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

const int MX = 1e6 + 5;
const int INF = 0x3f3f3f3f;

int A[MX];
int Q_1[MX], cur_1, tail_1;//Min
int Q_2[MX], cur_2, tail_2;//Max
int MIN[MX], MAX[MX];

int main() {
    int n, k; //FIN;
    while(~scanf("%d%d", &n, &k)) {
        k = min(n, k);
        cur_1 = tail_1 = cur_2 = tail_2 = 0;
        for(int i = 1; i <= n; i++) {
            A[i] = read();
        }

        for(int i = 1; i <= n; i++) {
            while(cur_1 < tail_1 && A[Q_1[tail_1 - 1]] > A[i]) tail_1--; Q_1[tail_1++] = i;
            while(cur_2 < tail_2 && A[Q_2[tail_2 - 1]] < A[i]) tail_2--; Q_2[tail_2++] = i;
            if(i >= k) {
                while(cur_1 < tail_1 && Q_1[cur_1] < i - k + 1) cur_1++;
                while(cur_2 < tail_2 && Q_2[cur_2] < i - k + 1) cur_2++;
                MIN[i - k + 1] = A[Q_1[cur_1]];
                MAX[i - k + 1] = A[Q_2[cur_2]];
            }
        }

        for(int i = 1; i <= n - k + 1; i++) {
            printf("%d%c", MIN[i], i == n - k + 1 ? '\n' : ' ');
        }
        for(int i = 1; i <= n - k + 1; i++) {
            printf("%d%c", MAX[i], i == n - k + 1 ? '\n' : ' ');
        }
    }
    return 0;
}


你可能感兴趣的:(单调队列 poj2823 Sliding Window)