单调队列


单调队列是对优先队列的改良。单调队列既可以从队首出队,也可以从队尾出队。
基本思想同样是维护队首元素作为答案,去掉 多余的元素(维护单调性)
队列里每一个元素用一个二元组表示(id,val),每次从队尾入队,删除队尾无用元素,保证编号递增,值递减
本质:id越大,val越大的数组越有用,所以删掉id和val都更小的元素(无用元素),也就是队内元素在列表中的顺序是单调递增的,队内的元素大小是单调递增或者递减的(自定义)。
用单调队列解决的问题是需要得到某个范围的最大值或者最小值
最后再介绍一下这个优先队列的作用,一般是用来优化DP,OI中较少直接仅仅考察该算法,同常都是和DP结合起来。

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

用数组

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct node{int x,index;}q,s[maxn],s_[maxn];
int a[maxn],n,k;
void min_v(){
	int front=1,back=0;
	for(int i=1;i<=n;i++){
		while(front<=back&&s[back].x>=a[i])back--;
		back++;
		s[back].index=i,s[back].x=a[i];
		if(s[front].index+k<=i)front++;
		if(i>=k)printf("%d ",s[front].x);
	}
}
void max_v(){
	int front=1,back=0;
	for(int i=1;i<=n;i++){
		while(front<=back&&s_[back].x<=a[i])back--;
		back++;
		s_[back].index=i,s_[back].x=a[i];
		if(s_[front].index+k<=i)front++;
		if(i>=k)printf("%d ",s_[front].x);
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	min_v();
	printf("\n");
	max_v();
	return 0;
}

用deque

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,k;
int a[maxn];
struct node{int index;int x;};
deque<node> q;
node t;
void min_v(){
	for(int i=1;i<=n;i++){
		t.index=i,t.x=a[i];
		while(!q.empty()&&q.back().x>=a[i])q.pop_back();
		q.push_back(t);
		while(q.front().index+k<=i)q.pop_front();
		if(i>=k)printf("%d ",q.front().x);
	}	
	q.clear();
}
void max_v(){
	for(int i=1;i<=n;i++){
		t.index=i,t.x=a[i];
		while(!q.empty()&&q.back().x<=a[i])q.pop_back();
		q.push_back(t);
		while(q.front().index+k<=i)q.pop_front();
		if(i>=k)printf("%d ",q.front().x);
	}
	q.clear();
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	min_v();
	printf("\n");
	max_v();
	return 0;
}

P1440 求m区间内的最小值

数组

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+10;
struct node{int index;ll x;}s[maxn];
ll a[maxn];
int n,m;
int main(){
	scanf("%d%d",&n,&m);
	int front=1,back=0;
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++){
		while(back>=front&&s[back].x>=a[i])back--;
		back++;
		s[back].x=a[i],s[back].index=i;
		if(i==1)
		printf("0\n");
		if(i>=m&&s[front].index+m<=i)front++;
		if(i!=n)
		printf("%lld\n",s[front].x);
	}
	return 0;
}

deque

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e6+10;
struct node{int index;ll x;}t;
int n,m;
ll a[maxn];
deque<node> q;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=n;i++){
		t.index=i,t.x=a[i];
		while(!q.empty()&&q.back().x>=a[i])
		q.pop_back();
		q.push_back(t);
		if(i==1)
		printf("0\n");
		if(q.front().index+m<=i)q.pop_front();
		if(i!=n)
		printf("%lld\n",q.front().x);
	}
	return 0;
}

P2032 扫描

数组

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
struct node{int x,index;}s[maxn];
int n,k;
int a[maxn];
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	int front=1,back=0;
	for(int i=1;i<=n;i++){
		while(front<=back&&s[back].x<=a[i])back--;
		back++;
		s[back].index=i,s[back].x=a[i];
		while(s[front].index+k<=i)front++;
		if(i>=k)
		printf("%d\n",s[front].x);
	}
	return 0;
} 

deque

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
struct node{
	int index,x;
}t;
int a[maxn];
int n,k;
deque<node> q;
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		t.index=i,t.x=a[i];
		while(!q.empty()&&q.back().x<=a[i])q.pop_back();
		q.push_back(t);
		while(q.front().index+k<=i)q.pop_front();
		if(i>=k)
		printf("%d\n",q.front().x);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值