Luogu-1886 (单调队列)

来源:互联网 发布:人体尺度数据测绘图 编辑:程序博客网 时间:2024/05/24 06:55

题目

https://www.luogu.org/problemnew/show/1886
P1886 滑动窗口

题目描述

现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

例如:
图
The array is [1 3 -1 -3 5 3 6 7], and k = 3.

输入一共有两行,第一行为n,k,第二行为n个数(<INT_MAX).

输出共两行,第一行为每次窗口滑动的最小值,第二行为每次窗口滑动的最大值

输入样例#1:

8 3
1 3 -1 -3 5 3 6 7

输出样例#1:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

说明

50%的数据,n<=10^5
100%的数据,n<=10^6

分析

  • 弄个单调队列,记录队列中元素的 id(在原数组中的下标) 以及数值。
  • 先求最小值吧:
  • 每次往队尾插入一个新元素 A 时,从队尾把比 A 大的全弹出来,保持队列单调。
  • 再判断一下队首元素的 id 是否越过了(小于)边界,越过了就出队。
  • 每次输出队首元素的数值即可。
  • 求最大值同理。
  • 时间复杂度 On。

程序

#include <cstdio>struct zzk{int v,id;} q[2000005];int i,n,k,l,r,A,a[1000005];int main(){    scanf("%d%d",&n,&k);    for (i=1; i<=n; i++) scanf("%d",&a[i]);    for (l=++r,i=1; i<=n; i++){        A=a[i];        if (i-q[l].id>=k) l++;        while (q[r].v>=A && r>=l) r--;        q[++r]=(zzk){A,i};        if (i>=k) printf("%d ",q[l].v);    }    for (puts(""),l=1,r=0,i=1; i<=n; i++){        A=a[i];        if (i-q[l].id>=k) l++;        while (q[r].v<=A && r>=l) r--;        q[++r]=(zzk){A,i};        if (i>=k) printf("%d ",q[l].v);    }}

提示

  • 这题也可以用线段树做,不过时间复杂度就多了个 log,练练手也可以。
  • RMQ 也可以做呢。