[Luogu 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).

 

输出格式:

 

输出共两行,第一行为每次窗口滑动的最小值

第二行为每次窗口滑动的最大值

 

这题是单调队列入门题。题意清晰明了,求区间最大(小)。第一反应是线段树或者RMQ,但是数据范围爆炸。这道题和普通的区间的区别就在于它的区间长度是固定的。所以使用时间复杂度为O(n)的单调队列来解决。

什么是单调队列呢?单调队列是一种特殊的双端队列,其内部具有单调性(有点像优先序列,但有着本质区别)。

如何实现插入?从队尾插入,若破坏了单调性,则删除队尾元素(这个删除决定了什么题能用什么题不能用),直到找到一个不影响的位置。

如何实现输出?访问队首(真是方便)

如何解决这道题?首先设一个区间为(l,r),则有max(l+1,r+1)=max(a[r+1],max(a[l+1],a[l+2]...a[r])),那么max(l+1,r+1)与max(l,r)其实是有很大一部分重叠的,那么在问题实现的时候就只需要先删除单调队列中不在区间里的数(a[l]),再插入新数(a[r+1]),剩余的不变,就可以解决了。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 inline int read(){
 6     int res=0,f=1;
 7     char ch=getchar();
 8     while(ch<'0'||ch>'9'){
 9         if(ch=='-')f=-1;
10         ch=getchar();
11     }
12     while(ch>='0'&&ch<='9'){
13         res=res*10+(ch-'0');
14         ch=getchar();
15     }
16     return res*f;
17 }
18 const int N=1e6+5;
19 int nsf[N],nbf[N],que[N],number[N],a[N];
20 int n,k,head,tail;
21 inline void INT(){
22     n=read();k=read();
23     for(int i=1;i<=n;++i)a[i]=read();
24 }
25 inline void findmin(){
26     head=1;tail=0;//队头、尾初始化 
27     for(int i=1;i<=n;++i){//插入a[i]到单调序列 
28         while(number[head]<i-k+1&&head<=tail)++head;
29         //从队首开始找,“过时”的删除 
30         while(a[i]<=que[tail]&&head<=tail)--tail;
31         //插入时从队尾插入,维护单调上升性质 
32         number[++tail]=i;que[tail]=a[i];
33         //number保存插入时的“时间戳”,que表示值 
34         nsf[i]=que[head];//当前队列中最小值 
35     }
36 }
37 inline void findmax(){
38     head=1;tail=0;
39     for(int i=1;i<=n;++i){
40         while(number[head]<i-k+1&&head<=tail)++head;
41         while(a[i]>=que[tail]&&head<=tail)--tail;
42         number[++tail]=i;que[tail]=a[i];
43         nbf[i]=que[head];
44     }
45 }
46 int main(){
47 INT();//输入 
48 findmin();//动态规划求单调队列最小值 
49 findmax();
50 for(int i=k;i<=n;++i)printf("%d ",nsf[i]);
51 cout<<endl;
52 for(int i=k;i<=n;++i)printf("%d ",nbf[i]);
53 return 0;
54 }

第一次写随笔还有点小兴奋呢~

posted @ 2019-03-16 10:08  clockwhite  阅读(182)  评论(0编辑  收藏  举报