• 【POJ 2823 Sliding Window】 单调队列


    题目大意:给n个数,一个长度为k(k<n)的闭区间从0滑动到n,求滑动中区间的最大值序列和最小值序列。

    最大值和最小值是类似的,在此以最大值为例分析。

    数据结构要求:能保存最多k个元素,快速取得最大值,更新时删去“过期”元素和“不再有希望”的元素,安放新元素。

    单调队列的基本概念百度百科讲得比较清楚了:http://baike.baidu.com/view/3771451.htm

    我的大致思路是:

    1. 每个元素存储为结构体,包含它的秩和值。维护最大长度为k的单调队列,保证所有元素的秩都在区间内,且从首到尾的元素,秩递增,值递减。

    2. 读入前k个元素(不存在过期问题),安放每个元素c:从队尾开始往回找到第一个大于它的元素g,将c放到g后面,c成为新的队尾

    3. 队首赋给最大值序列的第一个值。

    4. 读入k~n-1的元素,每读入一个元素c:

      (1)处理队首的过期元素(每次最多只可能是队首一个元素过期,因为队列长度不超过k,且秩是单调增的)

      (2)安放c(方法同前k个元素)

      (3)将新队首赋给最大值序列的下一个值

    5. 输出最大值序列

    想清楚了的话,代码还是比较好写的;队列没有封装,简单地用数组+首尾指针实现:

     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 
     5 struct Node
     6 {
     7     int index;
     8     int value;
     9 };
    10 
    11 Node max_q[2000002],min_q[2000002];
    12 int max_res[1000002],min_res[1000002];
    13 int front_max,front_min;//队首指针
    14 int back_max,back_min;//队尾指针
    15 int n,k,c;
    16 
    17 int main()
    18 {
    19     //freopen("c.txt","r",stdin);
    20     scanf("%d%d",&n,&k);
    21     back_max=back_min=front_max=front_min=0;
    22     scanf("%d",&c);
    23     max_q[back_max].value=c;
    24     min_q[back_min].value=c;
    25     max_q[back_max].index=0;
    26     min_q[back_min].index=0;//用第一个元素初始化
    27     for(int j=1;j<k;j++)//前k个元素
    28     {
    29         scanf("%d",&c);
    30         while(back_max>=0 && max_q[back_max].value<=c) back_max--;
    31         max_q[++back_max].value=c;
    32         max_q[back_max].index=j;
    33 
    34         while(back_min>=0 && min_q[back_min].value>=c) back_min--;
    35         min_q[++back_min].value=c;
    36         min_q[back_min].index=j;
    37 
    38     }
    39     max_res[0]=max_q[0].value;//区间起始位置的最值
    40     min_res[0]=min_q[0].value;
    41 
    42     for(int j=k;j<n;j++)//下标为k到n-1的元素
    43     {
    44         scanf("%d",&c);
    45         if(max_q[front_max].index==j-k) front_max++;
    46         if(min_q[front_min].index==j-k) front_min++;
    47 
    48         while(back_max>=front_max && max_q[back_max].value<=c) back_max--;
    49         max_q[++back_max].value=c;
    50         max_q[back_max].index=j;
    51 
    52         while(back_min>=front_min && min_q[back_min].value>=c) back_min--;
    53         min_q[++back_min].value=c;
    54         min_q[back_min].index=j;
    55 
    56         max_res[j-k+1]=max_q[front_max].value;//每读入一个元素,更新一次区间,得到一个最值
    57         min_res[j-k+1]=min_q[front_min].value;
    58     }
    59 
    60     for(int j=0;j<n-k+1;j++)
    61         printf("%d ",min_res[j]);
    62     printf("
    ");
    63     for(int j=0;j<n-k+1;j++)
    64         printf("%d ",max_res[j]);
    65     printf("
    ");
    66     return 0;
    67 }

    OJ的结果是这样的(G++会超时,尚不明原因):

    最开始没有考虑过期的问题,考虑之后担心队列不够长,需不需要写成循环的;但稍加分析会发现,front指针后移只发生在删除队首过期元素时,最多只发生n-k次,那么数组开到2n就可以了。

    由于是不循环的队列,只需front和back两个指针就可完成所有需要的操作。(之前因为和一个计数变量混用,并是在边界判断时WA了很多次)

    把前k个元素和之后的元素分开处理是为了考虑方便,AC了之后试图把它们合并起来然后并是又WA了。。。看来有时候不必过于追求代码的简炼,初学还是清晰更重要。

  • 相关阅读:
    简单介绍数据流动的控制
    数据再寄存器中的暂时保存
    八位三态数据通路控制器的逻辑电路设计讲解
    什么是同步有限状态机???
    verilog逻辑复制
    流水线经典讲解!!!!!
    流水线(pipe-line)简介
    总线(BUS)和总线操作
    多路选择器(multiplexer)简介
    使用telnet发送HTTP请求
  • 原文地址:https://www.cnblogs.com/helenawang/p/4769705.html
Copyright © 2020-2023  润新知