20201009 day30 复习2:滑动窗口

LGP1886 滑动窗口/【模板】单调队列

题意

给定长(n)的序列(a),以及大小为(k)的窗口。现在窗口从左向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。(kle n le 10^6,a_iin[-2^{31},2^{31}))

样例

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

(第一行最小值,第二行最大值)

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

题解

以样例为例。用(q)表示单调队列,(p)表示其所对应的在原来列表里的序号。

  1. 原来队列中没有一个元素,直接令1进队。此时q={1},p={1}.
  2. 下面基于这样一个思想:如果把3放进去,如果后面两个数(k=3)都比他大,就有可能成为最小的。此时q={1,3},p={1,2}.
  3. 下面出现了-1,队尾元素3比-1大,意味着只要-1进队,那么3一定不可能成为最小值【原因是框3的同时一定会框住-1】,所以3从队尾弹出。进行同样的思考,1也从对尾出队。最终-1进队,此时q={-1},p={3}.
  4. 出现-3,同上面分析,-1出队,-3进队,q={-3},p={4}。
  5. 出现5,因为5>-3,根据第二条分析,5还是有希望的,所以5进队。此时q={-3,5},p={4,5}
  6. 出现3,3与队尾的5比较,3<5,按照第3条的分析,5从队尾出队,3再与-3比较,同第二条,3进队。此时q={-3,3},p={4,6}
  7. 出现6.6与3比较,因为3<6,所以不必出队。由于3以前元素都<3,所以不必再比较,6进队,因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时q={3,6},p={6,7}
  8. 出现7.队尾元素6小于7,7进队。此时q={3,6,7},p={6,7,8}

code

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=1000001;
int n,k,a[maxn];
int q[maxn],head,tail,p[maxn];
int read(){
	int ans=0,op=1;
	char c;
	c=getchar();
	for(;(c<'0'||c>'9')&&c!='-';c=getchar());
	if(c=='-') op=-1,c=getchar();
	for(;(c>='0'&&c<='9');c=getchar()) ans*=10,ans+=c^48;
	return ans*op;
}
void init(){
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++) a[i]=read();
}
void findmax(){
	head=1,tail=0;
	for(int i=1;i<=n;i++){
		while(head<=tail&&q[tail]<=a[i]) tail--;
		q[++tail]=a[i];
		p[tail]=i;
		while(p[head]<=i-k) head++;
		if(i>=k) printf("%d ",q[head]);
	}
	printf("
");
}
void findmin(){
	head=1,tail=0;
	for(int i=1;i<=n;i++){
		while(head<=tail&&q[tail]>=a[i]) tail--;
		//只要队列里有元素,并且队尾元素比要处理的a[i]大,那么表示队尾元素已经不可能出场
		//所以出队,知道队尾元素小于待处理的a[i],满足单调
		q[++tail]=a[i];//处理值入队
		p[tail]=i;//同时存下编号
		while(p[head]<=i-k) head++;//如果队首元素已经不在窗口内,出队
		if(i>=k) printf("%d ",q[head]);
	}
	printf("
");
}
int main(){
	init();
	findmin();
	findmax();
	return 0;
}
要做就做南波万
原文地址:https://www.cnblogs.com/liuziwen0224/p/20201009day30-002.html