• 洛谷 P1886 滑动窗口(单调队列)


    题目链接

    https://www.luogu.org/problemnew/show/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

    解题思路

    先理解文意:对于给定一个长度为n的序列,找出所有长为k的区间的最大值(最小值)。

    首先很多人会想到,枚举每一个长为k的区间,然后遍历一遍,找到最大值(最小值),这样的时间复杂度是o(nk)的,显然超时

    所以我们需要换一种思路。

    单调队列:单调队列就是一个一直保持单调性(递增或递减)的长度最大为k的双端队列。

    我们维护这样一个单调队列,使它队首元素即为要求的最大值(最小值)。

    所以本题的核心是:怎样维护一个单调队列。

    在此举例求最大值:对于任意读入的元素,我们不放设为in,首先判断队列是否为空,如果队列为空,就一定要加入队列。若不为空,就一直比较队列末尾的元素,不放设为f,如果f<in,就把f弹出去,砍掉。为什么呢?因为f能做到的,in一定也能做到,in>f,所以在一定范围内,答案有可能是in,但永远不可能是f。(这里有一个有趣的类比,如果一位OIer比你年轻还比你强,那你就没法超越他了。——Kevin大佬)。还有一个问题,就是滑动窗口长度最大是k,所以我们需要用结构体保存每一个数的编号和数值,如果in的编号和队首的编号的差>=k,就把队首砍掉,这是显然的

    这样,由于每一个元素只进入队列一次,所以时间复杂度就变成o(n)了。

    话不多说,看代码。

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<string>
     6 #include<cstdlib>
     7 #include<queue>
     8 #include<set>
     9 #include<map>
    10 #include<vector>
    11 #include<algorithm>
    12 #include<iomanip>
    13 #include<ctime>                            //还是写了一大堆没用的头文件 
    14 using namespace std;
    15 int n,k;
    16 int maxx[1000005],minn[1000005];        //由于输出要求,需要先用数组存好答案 
    17 struct num{                                //结构体储存编号和数值 
    18     int cnt,value;
    19     num(int a,int b):cnt(a),value(b){}  //结构体构造函数,作用是在定义结构体的时候,就会给cnt和value赋值。用法具体看29行或者39行代码 
    20 };
    21 deque<num> q1,q2;                         //定义两个双端队列,q1储存最大值,q2储存最小值
    22 void makeq1(int i,int in){                //makeq1处理最大值 
    23     if(q1.empty()) q1.push_back(num(i,in));//队列为空时直接入队 
    24     else{
    25         num f=q1.front();
    26         if(i>f.cnt+k-1) q1.pop_front();//判断队列长度是否超过k 
    27         if(!q1.empty()){                //队尾弹不断弹出操作 
    28             num b=q1.back();
    29             while(b.value<in){
    30                 q1.pop_back();
    31                 if(q1.empty()) break;
    32                 b=q1.back();
    33             }
    34         }
    35         q1.push_back(num(i, in));
    36     }
    37 }
    38 void makeq2(int i,int in){                //makeq2处理最小值 
    39     if(q2.empty()) q2.push_back(num(i,in));//队列为空时直接入队 
    40     else{
    41         num f=q2.front();
    42         if(i>f.cnt+k-1) q2.pop_front();        //判断队列长度是否超过k 
    43         if(!q2.empty()){                    //队尾弹不断弹出操作 
    44             num b=q2.back();
    45             while(b.value>in){                //和makeq1不同的地方 
    46                 q2.pop_back();
    47                 if(q2.empty()) break;
    48                 b=q2.back();
    49             }
    50         }
    51         q2.push_back(num(i, in));
    52     }
    53 }
    54 int main()
    55 {
    56 cin>>n>>k;
    57 for(int i=1;i<=n;i++){
    58     int in;
    59     cin>>in;
    60     makeq1(i,in);
    61     makeq2(i,in);
    62     if(i>=k){                                //如果长度达到k,就储存结果 
    63         maxx[i]=q1.front().value;
    64         minn[i]=q2.front().value;
    65     }
    66 }
    67 for(int i=k;i<=n;i++) cout<<minn[i]<<" ";    //注意输出格式 
    68 cout<<endl;
    69 for(int i=k;i<=n;i++) cout<<maxx[i]<<" ";
    70 return 0;
    71 }
    AC代码
  • 相关阅读:
    Arduino学习笔记10
    Arduino学习笔记07
    Arduino学习笔记6
    Arduino学习笔记5
    Arduino学习笔记4
    Arduino学习笔记3
    linux下库文件的编程
    学习编程语言究竟学什么
    Arduino学习笔记2---数字温度计
    Arduino学习笔记0---开发板的了解
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/10492882.html
Copyright © 2020-2023  润新知