POJ2823 Sliding Window 【单调队列、单调栈初步】


【题目大意】

给你一个长度不超过1e6的序列,现在有一个数字k,表示长度为k的子序列自左到右滑动时保持长度为k。

现在要求出该长度为k区间在滑动的过程中区间的最大值最小值。

【解题思路】

如果此题直接模拟或者利用最大值的单调性来做的话,在长度为k的区间上无法保证次大值的正确性。

本题的正确的思路是单调队列。

首先求出每次滑动的最小值。

我们考虑维护一个下标队列,首先将前k的数直接加入队列。

然后用一个指针从k+1位扫到最后一位,遇到的数如果比要插入的数大,则删除队尾元素,将指针前移,重复操作知道该元素遇到一个比它大的元素,或队列为空。

记住保存到一定是下标。

然后重复以上操作求出最大值(将条件改为“遇到的数如果比要插入的数小”)

【代码】

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#define LL long long
//#define LOCAL
using namespace std;

namespace output{
    char ch[1000];
}

const int N=1000011;
int n,m,k;
int head,tail;
int q[N];
int a[N];

inline int CIN(){
	int num=0;
	int f=1;
	char c=getchar();
	while ((c>'9'||c<'0')&&c!='-') c=getchar();
	if (c=='-'){
		f=-1;
		c=getchar();
	}
	while (c>='0'&&c<='9'){
		num=(num<<3)+(num<<1)+c-'0';
		c=getchar();
	}
	return f*num;
}

inline void COUT(int x){
	if (x==0){
		putchar(48);
		return;
	}
	if (x<0){
		putchar('-');
		x=-x;
	}
	char *s=output::ch;
	while (x){
        *(++s)=x%10;
        x/=10;
	} 
	while (s!=output::ch) putchar((*(s--))+48);
}

struct humdrum{//下标队列 
	int q[N];
	bool exist[N];
	int zjl;
	
	void Insert_Max(int x){	
		while (tail>=head&&a[q[tail]]<=a[x]) exist[q[tail--]]=false;
		q[++tail]=x;
		exist[x]=true;
	}
	
	void Insert_Min(int x){	
		while (tail>=head&&a[q[tail]]>=a[x]) exist[q[tail--]]=false;
		q[++tail]=x;
		exist[x]=true;
	}
	
	void Delete(int x){
		if (exist[x]) head++;
	}
	
	void Clear(){
		memset(exist,false,sizeof(exist));
		memset(q,0,sizeof(q));
		zjl=0;
	}
}que;

int main(){
#ifdef LOCAL
    freopen("POJ2823.in","r",stdin);
#endif
	while (scanf("%d%d",&n,&k)!=EOF){
		que.Clear();
		head=0;
		tail=-1;
		for (int i=1;i<=n;++i) a[i]=CIN();
		
		for (int i=1;i<=k;++i) que.Insert_Min(i);
		COUT(a[que.q[head]]);
		putchar(' ');
		for (int i=k+1;i<=n;++i){
			que.Insert_Min(i);
			que.Delete(i-k);
			if (i!=n) COUT(a[que.q[head]]),putchar(' ');
			else COUT(a[que.q[head]]),putchar('\n');
		}
		
		que.Clear();
                head=0;
                tail=-1;
		for (int i=1;i<=k;++i) que.Insert_Max(i);
		COUT(a[que.q[head]]);
		putchar(' ');
		for (int i=k+1;i<=n;++i){
			que.Insert_Max(i);
			que.Delete(i-k);
			if (i!=n) COUT(a[que.q[head]]),putchar(' ');
			else COUT(a[que.q[head]]),putchar('\n');
		}
	}
	return 0;
}



【总结】

单调队列,单调栈可以优化很多问题,务必掌握!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值