【洛谷】P1886 滑动窗口 /【模板】单调队列

题目描述

有一个长为 nn 的序列 aa,以及一个大小为 kk 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

例如:

The array is [ 1 , 3 , − 1 , − 3 , 5 , 3 , 6 , 7 ] , a n d [1,3,-1,-3,5,3,6,7], and [1,3,1,3,5,3,6,7],and k = 3 k = 3 k=3

【洛谷】P1886 滑动窗口 /【模板】单调队列_第1张图片

输入格式

输入一共有两行,第一行有两个正整数 n,k。 第二行 n 个整数,表示序列 a

输出格式

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

输入输出样例

输入

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

输出

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

说明/提示

【数据范围】
对于 50% 的数据,1≤n≤105
对于 100% 的数据,1≤k≤n≤106 ,ai ∈[−231,231)


题解

单调队列模板题,单调递增和递减。

代码

一开始的手打队列代码看起来恨复杂,后来写题解看不下去 改进成queue。下面两种都有。

初始版手打队列代码:

#include
#include
#include
#define M 1001000
#define p(a) putchar(a+'0')
#define pin ch=getchar()
using namespace std;
int headu=1,headd=1,tailu=1,taild=1,k,n,u[M],d[M],a[M],mx[M],udel[M],ddel[M];
inline int read()   //快读
{
	register int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')w=-1;
		pin;
	}
	while(ch>='0'&&ch<='9')
	{
		s=(s<<1)+(s<<3)+(ch^48);
		pin;
	}
	return s*w;
}
inline void write(register int x)   //快输
{
	if(x<0)
	{
		p(-3);
		x=~(x-1);
	}
	int s[20],top=0;
	while(x)
	{
		s[++top]=x%10;
		x/=10;
	}
	if(!top) s[++top]=0;
	while(top) p(s[top--]);
	p(-16);
}
void in()   //输入部分
{
	n=read();
	k=read();
	for(register int i=1; i<=n; i++)
		a[i]=read();
	if(k==1)
	{
		for(register int i=1; i<=n; i++) write(a[i]);
		printf("\n");
		for(register int i=1; i<=n; i++) write(a[i]);
		exit(0);
	}
	return;
}
void up(register int x,register int i)   //单调递增
{
	for(; tailu>=headu&&u[tailu]>x; tailu--);
	u[++tailu]=x;
	udel[tailu]=i;
	if(i>=k)
	{
		while((i-k+1)>udel[headu]) headu++;
	}
	return;
}
void down(register int x,register int i)   //单调递减
{
	for(; taild>=headd&&d[taild]<x; taild--);
	d[++taild]=x;
	ddel[taild]=i;
	if(i>=k)
	{
		while((i-k+1)>ddel[headd]) headd++;
	}
	return;
}
int main()
{
	in();
	u[1]=d[1]=a[1];
	udel[1]=ddel[1]=1;
	for(register int i=2; i<=n; i++)
	{
		up(a[i],i);
		down(a[i],i);
		if(i<k) continue;
		write(u[headu]);
		mx[i]=d[headd];
	}
	p(-38);
	for(register int i=k; i<=n; i++) write(mx[i]);
	return 0;
}

改进版queue双端队列代码:

#include
#include
#include
using namespace std;
long long n,k,a[3000001];
deque<long long> up,down;
inline int read()   //快读
{
	register int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
inline void write(register int x)   //快输
{
	if(x<0)
	{
		putchar('-');
		x=~(x-1);
	}
	int s[20],top=0;
	while(x)
	{
		s[++top]=x%10;
		x/=10;
	}
	if(!top) s[++top]=0;
	while(top) putchar(s[top--]+'0');
}
int main()
{
	n=read();k=read();
	for(int i=1; i<=n; i++)  //单调递增求最小
	{
		a[i]=read();
		while(!up.empty()&&up.front()<=(i-k)) up.pop_front(); //弹出“过时”的即不在窗口里的元素
		while(!up.empty()&&a[up.back()]>a[i]) up.pop_back(); //弹出比a[i]大的元素,维护单调递增
		up.push_back(i); //将该元素下标推入up队列
		if(i>=k) write(a[up.front()]),putchar(' ');
	}
	cout<<endl;
	for(int i=1; i<=n; i++)  //单调递减求最大
	{
		while(!down.empty()&&down.front()<=(i-k)) down.pop_front(); //弹出“过时”的元素
		while(!down.empty()&&a[down.back()]<a[i]) down.pop_back(); //维护单调递减
		down.push_back(i); //将该元素下标推入down队列
		if(i>=k) write(a[down.front()]),putchar(' ');
	}
}

注:

不加快读快输会超时TTTTT

你可能感兴趣的:(#,队列,数据结构,队列,单调性,ssl,算法)