Luogu-1886 (单调队列)

题目

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 也可以做呢。

猜你喜欢

转载自blog.csdn.net/jackypigpig/article/details/78483870