单调队列 笔记

双端队列

介绍

双端队列(Deque)是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,相比list增加运算符重载。具体来说,双端队列允许两端都可以进行入队和出队操作。前端进元素排列在队列中后端进的元素前面,后端进的元素排列在队列中前端进的元素的后面。在双端队列入队时,前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面。在双端队列出队时,无论是前端还是后端出队,先出的元素排列在后出的元素的前面。

此外,双端队列既可以采用顺序存储,也可以采用链式存储实现。双端队列(Deque)既可说是Queue的子接口,也可说Stack(JDK并未提供这个接口)的子接口。因此,Deque即可当成队列使用,也可当成栈使用。

一般将队首进行的操作称之为压入和弹出,队尾进行的操作称之为插入和移除。

可以使用 STL 库中的<deque>实现双端队列。

常见函数

 empty()
 size()
 front()
 back()
 pop_back()
 pop_front()
 clear()
 push_back()
 push_front()

单调队列

单调队列便是用双端队列实现的

单调队列是一种特殊的双端队列,其中的元素遵循单调递增或单调递减的原则。在算法竞赛中,我们一般使用两个单调队列,一个维护单调递增序列,另一个维护单调递减序列。单调队列的队首元素保证是所求区间的最小(最大)值,由于每个元素最多进队和出队一次,因此在求解问题时可以保证时间复杂度为O(1)。

最小值:单调递增
最大值:单调递减

例题

滑动窗口

洛谷 P1886 滑动窗口

分别维护一个单调递增和递减的单调队列,队列内存储下标,每次入队后判断最小(最大)值是否在k范围之内,如不在,则出队。

#include<bits/stdc++.h>
using namespace std;
int n,ans,a[1000005],k,sum,minn[1000005],maxx[1000005];
deque<int> 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++){
		while(!q.empty()&&a[q.back()]>=a[i]){
			q.pop_back();
		}
		q.push_back(i);
		if(q.front()<i-k+1){
			q.pop_front();
		}
		minn[i]=a[q.front()];
	}
	q.clear();
	for(int i=1;i<=n;i++){
		while(!q.empty()&&a[q.back()]<=a[i]){
			q.pop_back();
		}
		q.push_back(i);
		if(q.front()<i-k+1){
			q.pop_front();
		}
		maxx[i]=a[q.front()];
	}
	for(int i=k;i<=n;i++){
		printf("%d ",minn[i]);
	}
	printf("\n");
	for(int i=k;i<=n;i++){
		printf("%d ",maxx[i]);
	}
	return 0;
}

切蛋糕

洛谷 P1714 切蛋糕

找到 sum[l-1]的最小值,使sum[r]-sum[l-1]最大

#include<bits/stdc++.h>
using namespace std;
int n,ans,a[500005],k,sum[500005],maxx=-0x3f3f3f;
int minn[500005];
deque<int> q;
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}

		for(int i=1;i<=n;i++){
			while(!q.empty()&&sum[q.back()]>=sum[i]){
				q.pop_back();
			}
			q.push_back(i);
			if(q.front()<i-k){
				q.pop_front();
			}
			minn[i]=sum[q.front()];
		}
		
	for(int i=1;i<=n;i++){
		maxx=max(maxx,sum[i]-minn[i]);
	}
	printf("%d",maxx);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值