单调队列的使用

定义

队列中的元素始终保持着单增或者单减的特性。
类似于单调栈

但是不同于单调栈,队列我们可以获取队列中数据的个数
注意是双端队列
于是我们就可以解决类似于“滑动窗口”的问题,区间最小值

示例看上一篇单调栈的

代码

deque<int> q; k为区间长度
while(!q.empty()&&q.back()>=x) q.pop_back();//这里是严格递增求区间最小值,改成<=求最大
	q.push_back(##code##);
	
	if(i-q.front()+1>k) q.pop_front();
	if(i>=k){//当满足区间长度时
		//##code##
}

题目

1,滑动窗口
滑动窗口
输入描述

输入由两行组成。第一行包含两个整数n和k,它们分别是数组和滑动窗口的长度。第二行有n 个整数。

输出描述

输出中有两行。第一行分别给出了窗口中每个位置的最小值,从左到右。第二行给出最大值。

输入样例

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

输出样例

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

数据范围
AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1000005;
deque<int> q;
int k,n,a[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	cin>>n>>k;
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		int x=a[i];
		while(!q.empty()&&a[q.back()]>x) q.pop_back();
		q.push_back(i);
		
		if(i-q.front()+1>k) q.pop_front();
		if(i>=k){
			cout<<a[q.front()]<<" ";
		}
	}
	cout<<endl;
	q.clear();
	for(int i=1;i<=n;i++){
		int x=a[i];
		while(!q.empty()&&a[q.back()]<x) q.pop_back();
		q.push_back(i);
		
		if(i-q.front()+1>k) q.pop_front();
		if(i>=k){
			cout<<a[q.front()]<<" ";
		}
	}
	
	return 0;
} 

理想正方形:
在这里插入图片描述
输出描述

仅一行一个整数,即小 Z 能够得到的最大幸运值。

输入样例

6 3
1 -2 3 -4 5 -6

输出样例

5

最 初省的一个题,因为是矩形,所以需要先一行一行处理区间最小值,然后再用同样方法处理列,思路简单但是不好写

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1005;
deque<int> Max,Min;
int k,n,m,ans=0x3f3f3f3f3f,a[N][N],r_max[N][N],r_min[N][N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	cin>>n>>m>>k;
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}	
	}
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			while(!Max.empty()&&a[i][Max.back()]<=a[i][j]) Max.pop_back();
			Max.push_back(j);
			if(j-Max.front()+1>k) 
				Max.pop_front();
			
			while(!Min.empty()&&a[i][Min.back()]>=a[i][j]) Min.pop_back();
			Min.push_back(j);
			if(j-Min.front()+1>k) 
				Min.pop_front();
			
			if(j>=k){
				r_min[i][j]=a[i][Min.front()];
				r_max[i][j]=a[i][Max.front()];//行的区间最小值
			}
		}
		Max.clear();Min.clear();
	}//先处理行
	
	for(int j=k;j<=m;j++){
		for(int i=1;i<=n;i++){
			while(!Max.empty()&&r_max[Max.back()][j]<=r_max[i][j]) Max.pop_back();
			Max.push_back(i);
			if(i-Max.front()+1>k) 
				Max.pop_front();
			
			while(!Min.empty()&&r_min[Min.back()][j]>=r_min[i][j]) Min.pop_back();
			Min.push_back(i);
			if(i-Min.front()+1>k) 
				Min.pop_front();
			
			if(i>=k){
				ans=min(ans,r_max[Max.front()][j]-r_min[Min.front()][j]);//统计答案
			}
		}
		Max.clear();Min.clear();
	}在处理列
	cout<<ans;
	return 0;
} 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值