CSP-training Week5 Problem B 单调队列问题(POJ - 2823 )

Week5 Problem B 单调队列问题

数据结构概述

队列,一种常见的数据结构,他模拟了我们日常生活中的队列数据结构,具有实际生活中队列的特点:先来先进入队列,队列的前段可以先接受服务。作为一种常用且基础的数据结构,c++的STL库也提供了封装好的队列结构,可以使用下面的语句来进行封装类型queue的使用

#include<queue>

关于STL中队列的具体方法,可以查询c++官方文档进行学习:c++官方文档
在题目中将使用一种队列的延伸数据结构–单调数据结构,并用他来解决一定区域内进行最大最小值搜索等数值比较问题。

题目概述

题目叙述

ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.

窗口状态 最小值 &最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7

INPUT & 输入样例

输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
输入样例:

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

OUTPUT & 输出样例

输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
输出样例:

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

题目重述和坑点

将题意简述可以缩略成下列要求:给定一个数值序列,和一个窗口的大小,将窗口以step=1的速度向后移动,计算出每个窗口状态下,窗口内的最大值和最小值。

解题思路

如果你熟悉单调队列,应该会发现这道题在明白题意后是一道简单明了的单调队列板子题,因为他有下述几个关键字:
1、一个数值的序列,且顺序访问。
2、要搜索最大值、最小值的范围是一个局部区域(窗口)
3、要处理的数据比较简单,能够直接比较大小

为什么要用单调队列:

综合题意可知,该问题的主要要求有两个:求出最大(最小)值&维持窗口大小,保持单调性我们可以采取类似于单调栈的解法:维持一个单调递增(递减)序列,通过插入弹出的方法维持数值的有序,具体内容可以见:单调栈内容详解
但是另一个要求是我们已有的数据结构无法满足的:保持窗口的大小不变。

单调队列具体操作

因此我们引入单调队列(单调且可以从头部、尾部进行操作)
要求1解决方案:单调性的维持,我们可以将单调栈的维持方法搬过来放在队列尾部的维持,即可维护队列始终保持单调
要求2解决方案:要维护一个队列中存放的值不超出窗口,可以使用下标队列来进行标定,在队列中存放每个元素的真实数组下标,一旦存在
队尾新插入元素下标-队首元素下标>=窗口大小
证明窗口移动后,队首的元素已经不属于当前窗口,可以弹出。
通过如上维持单调队列的做法,我们可以保证队列的队首始终是当前窗口合法的(符合窗口大小的)最大值(最小值),问题得以解决。

总结

一道单调队列板子题,但是通过这道题可以很好得学习单调队列的中心思想:
1、尾部维持单调性
2、头部维持搜索区域的大小
单调队列和单调栈常常混合出题,他们分别解决了在一个数值序列中进行局部搜索和全局搜索的问题,可以对比理解使用。

题目源码

#include<iostream>
#include<cstdio>
using namespace std;
int q[1000010];
int a[1000010];
int max_num[1000010];
int min_num[1000010];
int number=0;
int win=0;
void make_max()//维持递减数列
{
    int L=1;
    int R=0;
    for(int i=1;i<=number;i++)
    {
        while(R>=L && a[q[R]]<=a[i])
        {R--;}
        q[++R]=i;
        if(q[R]-q[L]>win-1)
        L++;
        max_num[i]=q[L];
    }
}
void make_min()//维持递增数列数列
{
    int L=1;
    int R=0;
    for(int i=1;i<=number;i++)
    {
        while(R>=L && a[q[R]]>=a[i])
        {R--;}
        q[++R]=i;
        if(q[R]-q[L]>win-1)
        L++;
        min_num[i]=q[L];
    }
}
int main()
{
    scanf("%d %d",&number,&win);
    for(int i=1;i<=number;i++)
    {
        scanf("%d",&a[i]);
    }
    make_max();
    make_min();
    for(int i=win;i<=number;i++)
    {
        printf("%d ",a[min_num[i]]);
    }
    printf("\n");
    for(int i=win;i<=number;i++)
    {
        printf("%d ",a[max_num[i]]);
    }
    printf("\n");
    
    return 0;
}
发布了17 篇原创文章 · 获赞 2 · 访问量 1656

猜你喜欢

转载自blog.csdn.net/qq_43942251/article/details/105160021