POJ 2823 Sliding Window + 单调队列
一、概念介绍
1、 双端队列
双端队列是一种线性表,是一种特殊的队列,遵守先进先出的原则。双端队列支持以下4种操作:
- (1) 从队首删除
- (2) 从队尾删除
- (3) 从队尾插入
- (4) 查询线性表中任意一元素的值
2、 单调队列
单调队列是一种特殊的双端队列,其内部元素具有单调性。最大队列与最小队列是两种比较常用的单调队列,其内部元素分别是严格单调递减(不是非递增)和严格单调递增(不是非递减)的。
单调队列的常用操作如下:
- (1) 插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再破坏单调性为止,再将其插入单调队列。
- (2) 获取最优(最大、最小)值:访问队首元素
以下是一个单调递增队列的例子:
队列大小不能超过3,入队元素依次为3,2,8,4,5,7,6,4
3入队:(3)
3从队尾出队,2入队:(2)
8入队:(2,8)
8从队尾出队,4入队:(2,4)
5入队:(2,4,5)
2从队头出队,7入队:(4,5,7)
7从队尾出队,6入队:(4,5,6)
6从队尾出队,5从队尾出队,4从队尾出队,4入队:(4)
以上左端为队头,右端为队尾。
二、单调队列的应用
1、最大值的维护:
比如我们要维护一个区间为k的最大值的单调队列,由于新插入 的节点他的“生命力”肯定比原先已经在队列中的元素“活”的时间长,将插入元素不断与队尾元素比, 如果他大于队尾元素,那么tail--将队尾元素删掉,(因为目前插入的这个元素值(设为pos)更大,而且“活”的时间 长,有pos在,队尾元素的有“生”之年永远都没法为最大值,故而直接无视比pos小的队尾了)。直到对空位置或者 找到了一个比pos大的队尾。
2、poj 2823 Sliding Window
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 by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position | Minimum value | Maximum value |
---|---|---|
[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 |
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input
8 3 1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3 3 3 5 5 6 7
思路
一、
这道题目有个非常重要的信息,即所有的区间都是等长且连续的,那么对于“相邻”两个区间(l,r)与(l+1,r+1)有些极优美的性质:al,al+1,al+2,…,ar-1,ar,ar+1,以最大值为例:我们注意到,在区间(l,r)中,Max(al,al+1,al+2,…,ar-1,ar)=max(al,max(al+1,al+2,…,ar-1,ar))
在区间(l+1,r+1)中,Max(al+1,al+2,…,ar-1,ar,ar+1)=max(max(al+1,al+2,…,ar-1,ar),ar+1)
两个式子中有相同的部分max(al+1,al+2,…,ar-1,ar),经验告诉我们,区间(l,r)中最大值落在(l+1,r)区间的概率很大。那么,在求(l+1,r+1)的最值时,我们完全没有必要再扫描一次。只有当上一次的最大值落在了al上时才需要重新扫描,这样,算法得到了极大的优化。
继续考虑这样一个问题,以最大值为例,对于任意l<=i<=j<=r,如果ai<aj,那么,在区间向右移动的过程中,最大值永远也不会落在ai上,因为ai比aj先失效,能用ai一定能用aj,此时我们便不再需要ai了。这个性质与单调队列性质重合了。在扫描这个数列的时候,我们维护一个单调递减的队列。当我们将区间从(l,r)移动到(l+1,r+1)时,将ar+1 插入单调队列,若队首元素不在(l+1,r+1)时,删除它。这样处理后的队首元素便是(l+1,r+1)区间中的最大值。
二、
看这个问题: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 by one position.Your task is to determine the maximum and minimum values in the sliding window at each position.
也就是有一个数列a,要求你求数列b和c,b[i]是a[i]…a[i+w-1]中的最小值,c[i]是最大值。如果a是1,3,-1,-3,5,3,6,7,则b为-1,-3,-3,-3,3,3,c为3,3,5,5,6,7。
这个问题相当于一个数据流(数列a)在不断地到来,而数据是不断过期的,相当于我们只能保存有限的数据(sliding window中的数据,此题中就是窗口的宽度w),对于到来的查询(此题中查询是每时刻都有的),我们要返回当前滑动窗口中的最大值最小值。注意,元素是不断过期的。
解决这个问题可以使用一种叫做单调队列的数据结构,它维护这样一种队列:
- a)从队头到队尾,元素在我们所关注的指标下是递减的(严格递减,而不是非递增),比如查询如果每次问的是窗口内的最小值,那么队列中元素从左至右就应该递增,如果每次问的是窗口内的最大值,则应该递减,依此类推。这是为了保证每次查询只需要取队头元素。
- b)从队头到队尾,元素对应的时刻(此题中是该元素在数列a中的下标)是递增的,但不要求连续,这是为了保证最左面的元素总是最先过期,且每当有新元素来临的时候一定是插入队尾。
满足以上两点的队列就是单调队列,首先,只有第一个元素的序列一定是单调队列。
那么怎么维护这个单调队列呢?无非是处理插入和查询两个操作。
对于插入,由于性质b,因此来的新元素插入到队列的最后就能维持b)继续成立。但是为了维护a)的成立,即元素在我们关注的指标下递减,从队尾插入新元素的时候可能要删除队尾的一些元素,具体说来就是,找到第一个大于(在所关注指标下)新元素的元素,删除其后所有元素,并将新元素插于其后。因为所有被删除的元素都比新元素要小,而且比新元素要旧,因此在以后的任何查询中都不可能成为答案,所以可以放心删除。
对于查询,由于性质b,因此所有该时刻过期的元素一定都集中在队头,因此利用查询的时机删除队头所有过期的元素,在不含过期元素后,队头得元素就是查询的答案(性质a),将其返回即可。由于每个元素都进队出队一次,因此摊销复杂度为O(n)。
#include<stdio.h> #include<string.h> const int maxn = 1000005; int a[maxn],q[maxn]; int n,k; void Monotonequeue(int op) { memset(q,0,sizeof(q)); bool first = true; int head = 1,tail = 0; for (int i = 1; i <= n; i++) { while (head <= tail && ((!op && a[i] <= a[q[tail]]) || (op && a[i] >= a[q[tail]]))) tail--; q[++tail] = i; while (head <= tail && q[tail] - q[head] >= k) head++; if (i >= k) first?printf("%d",a[q[head]]):printf(" %d",a[q[head]]),first = false; } printf("\n"); } int main() { scanf("%d%d",&n,&k); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); } Monotonequeue(0); //维护单调递增队列,队头元素最小 Monotonequeue(1); //维护单调递减队列,队头元素最大 return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 LL; const int maxn = 1000005; struct node { int pos,val; } q[maxn]; int num[maxn],n,k; int Min[maxn]; int Max[maxn]; void getMin() { int head=1,tail=0; for(int i=1; i<k; i++) { //前k-1个数跟查询无关,直接放进来 while(head<=tail&&num[i]<q[tail].val) tail--; //插入的准备操作,要么队列为空,要么找到一个位置使得符合单调队列定义 tail++; q[tail].val=num[i]; q[tail].pos=i; //插入 } for(int i=k; i<=n; i++) { while(head<=tail&&num[i]<q[tail].val) tail--; tail++; q[tail].val=num[i]; q[tail].pos=i; while(i-q[head].pos>=k) head++; //删除操作,维护最大的区间长度 Min[i-k]=q[head].val; //答案记录 } } void getMax() { int head=1,tail=0; for(int i=1; i<k; i++) { while(head<=tail&&num[i]>q[tail].val) tail--; tail++; q[tail].val=num[i]; q[tail].pos=i; } for(int i=k; i<=n; i++) { while(head<=tail&&num[i]>q[tail].val) tail--; tail++; q[tail].val=num[i]; q[tail].pos=i; while(i-q[head].pos>=k) head++; Max[i-k]=q[head].val; } } int main() { while(scanf("%d%d",&n,&k)!=EOF) { for(int i=1; i<=n; i++) scanf("%d",&num[i]); getMin(); for(int i=0; i<=n-k; i++) printf("%d%c",Min[i],i==n-k?'\n':' '); getMax(); for(int i=0; i<=n-k; i++) printf("%d%c",Max[i],i==n-k?'\n':' '); } return 0; }
POJ 2823 Sliding Window + 单调队列的更多相关文章
- poj 2823 Sliding Window (单调队列入门)
/***************************************************************** 题目: Sliding Window(poj 2823) 链接: ...
- POJ 2823 Sliding Window (单调队列)
单调队列 加了读入挂比不加更慢.... 而且这份代码要交c++ 有大神G++跑了700ms..... orzorzorz #include<iostream> #include<cs ...
- poj 2823 Sliding Windows (单调队列+输入输出挂)
Sliding Window Time Limit: 12000MS Memory Limit: 65536K Total Submissions: 73426 Accepted: 20849 ...
- POJ 2823 Sliding Window 题解
POJ 2823 Sliding Window 题解 Description An array of size n ≤ 106 is given to you. There is a sliding ...
- 洛谷P1886 滑动窗口(POJ.2823 Sliding Window)(区间最值)
To 洛谷.1886 滑动窗口 To POJ.2823 Sliding Window 题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每 ...
- POJ 2823 Sliding Window(单调队列入门题)
Sliding Window Time Limit: 12000MS Memory Limit: 65536K Total Submissions: 67218 Accepted: 190 ...
- POJ 2823 Sliding Window 【单调队列】
题目链接:http://poj.org/problem?id=2823 题目大意:给出一组数,一个固定大小的窗体在这个数组上滑动,要求出每次滑动该窗体内的最大值和最小值. 这就是典型的单调队列,单调队 ...
- 【单调队列】poj 2823 Sliding Window
http://poj.org/problem?id=2823 [题意] 给定一个长度为n的序列,求长度为k的滑窗内的最大值和最小值 [思路] 裸的单调队列 注意用C++提交,不然会T,orz我用G++ ...
- 题解报告:poj 2823 Sliding Window(单调队列)
Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is m ...
随机推荐
- web文档在线阅览
之前遇到很多各种文档在线阅览的需求,也有不少朋友经常问我这种需求的实现方案,大致试了一下网上的一些比较主流的推荐方案,但都不尽如人意,这里有一个比较全面的总结,需要的朋友可以根据自己的需求到这里查看, ...
- 【JQuery】jQuery.inArray 确定第一个参数在数组中的位置
函数:jQuery.inArray(value,array,[fromIndex]) 解释: value:用于在数组中查找是否存在 array:待处理数组. ...
- Dubbo系列(3)_官方Demo说明
一.本文目的 通过Dubbo的官方Demo介绍,学会搭建一个简单的Dubbo程序,包括服务端.客户端.接口等. Demo地址:https://github.com/alibaba/dubbo/ ...
- 关于 HTTP 请求头的内容
HTTP(HyperTextTransferProtocol)即超文本传输协议,目前网页传输的的通用协议.HTTP协议采用了请求/响应模型,浏览器或其他客户端发出请求,服务器给与响应.就整个网络资源传 ...
- linux-查看系统是32位还是64位
可以用命令“getconf LONG_BIT”查看, 如果返回的结果是32则说明是32位,返回的结果是64则说明是64位. 此外还可以使用命令“uname -a”查看, 输出的结果中,如果有x86_6 ...
- java-Date、String、Calendar转化
1.Calendar 转化 String //获取当前时间的具体情况,如年,月,日,week,date,分,秒等 Calendar calendat = Calendar.getInstance(); ...
- 怎么学习AOPR使用方法
Advanced Office Password Recovery作为一款专业级别的Office密码破解工具,对十几种office文档的密码都可以有效的破解.一些用户朋友才开始接触Advanced O ...
- ArrayList实现线程的几种方法
第一种.给方法名加synchronized Public synchronized void method(){ //-. } 第二种 New synchronized arraylist(); 第三 ...
- c# base64 编码解码
一. Base64的编码规则 Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编 码.它将需要编码的数据拆分成字节数组.以3个字节为一组.按顺序排列24 位数据,再把这24位数 ...
- C++ 复制控制之复制构造函数
7月26日更新: 过了这么长的时间回过头来看,发现文章中有几个点说错(用红字标出): 构造函数不是只有唯一一个参数,它也可以是多参数形式,其第二参数及后继以一个默认值供应. 不是没有声明复制控制函数时 ...