题解 P1886 【滑动窗口】

· · 题解

这个就直接用单调队列来解决就好啦!

单调队列,即单调递减或单调递增的队列。

并且单调队列与普通队列的区别在于它既可以从队首出列,也可以从队尾出列。

那么我们应该怎样实现单调队列呢?

其实很简单,我们可以用某种叫做双端队列的东西。

双端队列的基本操作:

(1) deque<int> dq 定义一个int类型的双端队列dq

  (2) dq.push_back(x) 将x放入dq的末端

  (3) dq.push_front(z) 将x放入dq的前端

  (4) dq.pop_front() 弹出队列的前端元素

(5) dq.pop_back() 弹出队列的后端元素

  (6) dq.front() 返回队列的前端元素

(7) dq.back() 返回队列的后端元素

之后这个,,题目的解法,,其实就很简单啦!!!

先从单调递增来说:

由于我们要找较小的那个数,所以放两个数同时在区间内的时候,下标较小并且数值较大的数就没有用了!!为了减少搜索次数,就重新开一个空间,放我们需要的数,把下标较小并且数值较大的数删去!!

接下来就是实现方法:

  1. 先让第一个数进队。

  2. 在下一个数进队之前, 进行判断:

    1.在判断之前要看队列是否为空!!!如果空了它会找不到队首的数,那么就会报错!!

    2.此时队首是否要删去。

    3.队尾的数是否比下一个要推进队列里的数大。

  3. 将下一个数推进队列。

大概就是这样了

当输出的时候就要注意:

当推进去的个数没有到达k的时候,,不需要输出!!!

对了,差点忘了,,单增与单减的区别其实就是把大于号改成小于号就行啦

好啦下面就是代码:

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000005
int a[maxn];
int n,k;
struct node{//由于我们在判断队首是否需要删去的时候要利用下标,,所以开个结构体更加方便!
    int pos,val;//pos就是下标了,val就是数值。
};
deque<node> b;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){//单调递增队列
        if(!b.empty()&&i-b.front().pos>=k)
            b.pop_front();
        while(!b.empty()&&b.back().val>=a[i])
            b.pop_back();
        node t;t.pos=i;t.val=a[i];
        b.push_back(t);
        if(i>=k){
            printf("%d ",b.front().val);
        }
    }
    printf("\n");//别忘了这个哦
    b.clear();//由于之前的队列中仍然还存在着数,,所以这里需要清空,当然啦,你再开一个双端队列也是可以的。
    for(int i=1;i<=n;i++){//单调递减队列
        if(!b.empty()&&i-b.front().pos>=k)
            b.pop_front();
        while(!b.empty()&&b.back().val<=a[i])
            b.pop_back();
        node t;t.pos=i;t.val=a[i];
        b.push_back(t);
        if(i>=k){
            printf("%d ",b.front().val);
        }
    }
    return 零;//你懂得!
}