cogs 495. 滑动窗口 单调队列

495. 滑动窗口

★★   输入文件:window.in   输出文件:window.out   简单对比
时间限制:2 s   内存限制:256 MB

【问题描述】

给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:

Window position Min value Max value
[1 3 -1] -3 5 3 6 7 -1 3
[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

你的任务是找出窗口在各位置时的max value,min value.

【输入格式】

第一行n,k,第二行为长度为n的数组

【输出格式】

第一行每个位置的min value,第二行每个位置的max value

【输入输出样例】

window.in
8 3
1 3 -1 -3 5 3 6 7

window.out
-1 -3 -3 -3 3 3
3 3 5 5 6 7

【数据范围】

20%:n≤500; 50%:n≤100000;100%:n≤1000000;

多年未敲(不禁心生疑惑 我一共才学了几年!?)的单调队列准备拿出来复习一下啦这一道滑动窗口就是一道非常经典的入门题啦(天哪 我还在刷入门题)

首先单调队列到底是什么呢?首先来介绍一个stl的好东西

deque<int> q;

这是什么东东?

这个叫双端队列 我们平常的队列QUEUE都是右边进  右边出的

这个双端队列就是左右都能进出啦

(当然也可以手动建一个数组来手动模拟)

那么讲了半天还是没有讲到单调队列是什么

就是进行一些操作来保证队列的单调性

单调性又是什么?

其实也就是队列中的元素都保持单调递增或单调递减

理论知识终于讲完了

现在我们来讲一下如何用这样一个单调递增或递减的可以两端进出的单调队列来解决这一道题吧

我们先来拿区间的最小值来举例吧 这是的单调队列是递增的 排在最前面的是最小的

首先我们把第一个元素放入单调队列中

接下来我们从第二个元素来进行操作

首先我们先把第二个元素加入到队列当中(假如它比第一个元素小)

我们先从前往后枚举 用一个while循环 先把比当前第二个元素要小的全部从队列里清除

很显然现在就只剩下第二个元素自己了

假如要枚举的区间长度是2  那么它就是第一个区间的最小值 我们再找一个数组来记录以当前枚举到的位置为区间结束位置的区间的最小值即可

也就是说我们把队尾那些较大的不可能成为区间最小值的popback一下

同时我们还要在进行一个操作 就是把过时的元素全部从队列里踢出去

比如枚举到了第4个元素 区间的长度是2   假如2现在还在队列里之前一直没有被踢掉 它就不属于当前枚举的区间了  我们就要把它从队列里踢出去

那么怎样知道应该踢出去谁呢?首先2这个节点肯定是队列中现存的位置中最靠前的 也就是说他是最先放进去的  但是却一直没有被更新掉 仔细地想一想 它肯定是在队列的最前面

我们只需要popfront一下就行了

此时我们维护的单调队列是递增的  也就是说最小的排在最前面 我们在取最小值的时候只需要把单调队列中的队首元素取出来就行了

至于区间最大值 也是基本类似的  只是单调队列变成了递减的  排在队首的是最大的

具体看代码 我相信您肯快就会明白 我这么弱的都看懂了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<deque>
using namespace std;
int v[1000005],minn[1000005],maxx[1000005];
int n,k;
void Min()//递增
{
    deque<int> q;
    int i;
    q.push_back(1);
    minn[1]=v[1];
    for(int i=2;i<=n;i++)
    {
        while(!q.empty()&&v[q.back()]>v[i])
            q.pop_back();//把队尾不合法的都弹出去反正也不会是最小的,因为有比他们更小的
        q.push_back(i);//放进去
        while(!q.empty()&&q.front()<i-k+1)
            q.pop_front();//把过时的都弹出去
        minn[i]=v[q.front()];//剩下的合法的最小的是队头
    }

}
void Max()//递减
{
    deque<int> q;
    int i;
    q.push_back(1);
    maxx[1]=v[1];
    for(int i=2;i<=n;i++)
    {
        while(!q.empty()&&v[q.back()]<v[i])
            q.pop_back();
        q.push_back(i);
        while(!q.empty()&&q.front()<i-k+1)
            q.pop_front();
        maxx[i]=v[q.front()];
    }
}
int main()
{

    scanf("%d%d",&n,&k);

    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    Min();
    Max();
    for(int i=k;i<n;i++)
        printf("%d ",minn[i]);
    printf("%d\n",minn[n]);
    for(int i=k;i<n;i++)
        printf("%d ",maxx[i]);
    printf("%d",maxx[n]);
    return 0;
}

原文地址:https://www.cnblogs.com/Tidoblogs/p/11409813.html

时间: 2024-10-09 11:56:54

cogs 495. 滑动窗口 单调队列的相关文章

luoguP1886 滑动窗口 [单调队列]

题目描述 现在有一堆数字共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 -

洛谷P1886 滑动窗口 单调队列

洛谷P1886 滑动窗口 单调队列 求一个固定长度的区间 最小值和最大值 单调队列求最小值时 1.刚要插入一个数 判断 其是否 小于等于 队尾的数 如果是 则将队尾的数出队 因为求的是队尾到之前的 最小值 ,所以其已经不可能成为 最小值了2.然后数字进队 3.如果队头 已经不再这个区间中,那就队头出队 最大值也是同理 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cs

[BZOJ 1047][HAOI 2007]理想的正方形(二维滑动窗口+单调队列)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1047 思路:裸的二维上的滑动窗口问题,可以借鉴一维滑动窗口的思路.首先预处理出每一列j的.以第i行元素为结尾.长度为n的区间的最大值maxv[i][j].最小值minv[i][j],然后再搞每一行,求出以每一行i结尾.行标上长度为n的区间.以第j列结尾.列标上长度为n的区间得到的二维区间最大值与最小值之差,遍历每一行得到这个差的最小值即为答案. #include <iostrea

Acwing 154 滑动窗口(单调队列)经典模板

给定一个大小为n≤106n≤106的数组. 有一个大小为k的滑动窗口,它从数组的最左边移动到最右边. 您只能在窗口中看到k个数字. 每次滑动窗口向右移动一个位置. 以下是一个例子: 该数组为[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]

POJ 2823 滑动窗口 单调队列

模板题 以后再写= = 还需要二分优化(减少常数?) #include<iostream> #include<cstdio> using namespace std; #define REP(r,x,y) for(register int r=(x); r<(y); r++) #define REPE(r,x,y) for(register int r=(x); r<=(y); r++) #ifdef sahdsg #define DBG(...) printf(__V

单调栈与单调队列

单调栈 特点 栈内的元素单调递增或者单调递减,可以在\(O(n)\)的时间内求出数列中所有数的左边或右边第一个比其大或小的元素,总时间复杂度为\(O(n)\) 例子 单调栈中一般存索引 一个单调递增栈s = [0, 10, 20 ,t]代表栈中a[1]~a[9]的元素大于a[10]的元素,索引为a[11]~a[19]的元素大于a[20]的元素... 这样我们可以发现在a[10]左边第一个比a[10]的数为a[0],在a[20]左边第一个比a[20]的数为a[10]... 如何实现呢? 每次有元素

详解--单调队列 经典滑动窗口问题

单调队列,即单调的队列.使用频率不高,但在有些程序中会有非同寻常的作用. 动态规划·单调队列的理解 做动态规划时常常会见到形如这样的转移方程: f[x] = max or min{g(k) | b[x] <= k < x} + w[x] (其中b[x]随x单调不降,即b[1]<=b[2]<=b[3]<=...<=b[n]) (g[k]表示一个和k或f[k]有关的函数,w[x]表示一个和x有关的函数) 这个方程怎样求解呢?我们注意到这样一个性质:如果存在两个数j, k,使

滑动窗口的单调队列

今天的训练赛HDU 4122,卡到最后也没出来,结束后和队友冷静分析代码后才发现错在一个简单的错误上,修改后A了 赛后看题解,大家的题解中大都提到了要用单调队列. 去网上搜单调队列..文章无外乎就两种..= =  抄袭好严重呀 1.http://zhonghuan.info/2014/09/16/%E6%B5%85%E6%9E%90%E5%8D%95%E8%B0%83%E9%98%9F%E5%88%97/ 简单的单调队列 2.http://blog.pureisle.net/archives/4

单调队列(滑动窗口问题)

Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards b