「学习笔记」单调队列与单调栈

「学习笔记」单调队列与单调栈

点击查看目录

单调队列

一个具有单调性的队列。插入一个元素时,如果直接插入不满足单调性,就一直从后弹出,直到插入后满足单调为止。一般用双端队列实现。
(stl里有双端队列deque,但常数较大,建议手写)

Examples:

luogu P1886/Loj P10175

Meaning of the Problem

给你一个数列 \(a\) ,多组长度为 \(k\) 的区间的最大值与最小值。

Solution

一道非常经典的单调队列题。

以最大值为例:

插入一个数时,如果直接插入不满足递减,就一直弹出,直到插入后满足递减为止。如果队头存的下标超出了这个区间就弹出。最后队头就是最大值。

最小值思路差不多,不说了。

Code
#include <bits/stdc++.h>
#define _for(i,a,b) for(int i=a;i<=b;++i)
#define for_(i,a,b) for(int i=a;i>=b;--i)
#define ll long long
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f;
int n,k,a[N],ans;
int q1[N],h1,t1,q2[N],h2,t2;
int main(){
	scanf("%d%d",&n,&k);
	_for(i,1,n)scanf("%d",&a[i]);
	_for(i,1,k-1){
		while(h1<=t1&&a[q1[t1]]>=a[i])--t1;
		while(h2<=t2&&a[q2[t2]]<=a[i])--t2;
		q1[++t1]=i,q2[++t2]=i;
	}
	_for(i,k,n){
		while(h1<=t1&&a[q1[t1]]>=a[i])--t1;
		while(q1[h1]<=i-k)++h1;
		q1[++t1]=i;
		printf("%d ",a[q1[h1]]);
	}printf("\n");
	_for(i,k,n){
		while(h2<=t2&&a[q2[t2]]<=a[i])--t2;
		while(q2[h2]<=i-k)++h2;
		q2[++t2]=i;
		printf("%d ",a[q2[h2]]);
	}
	return 0;
}

luogu P2216/Loj P10182

Meaning of the Problem

在一个 \(a*b\) 的矩阵中找一个 \(n*n\) 的矩阵,使该矩阵中最大值与最小值差最小。

Solution

我们可以先用单调队列维护出来长度为 \(n\) 的横条最大值和最小值,再去算出矩阵的最大值和最小值。时间复杂度 \(O(abn)\)

Code
#include <bits/stdc++.h>
#define _for(i,a,b) for(int i=a;i<=b;++i)
#define for_(i,a,b) for(int i=a;i>=b;--i)
#define ll long long
using namespace std;
const int N=1e3+10,inf=0x3f3f3f3f;
int a,b,n,ans=inf;
int mt[N][N],ax[N][N],in[N][N];
int q1[N],q2[N],h1,h2,t1,t2;
void line(){
	_for(i,1,a){
		_for(j,1,n-1){
			while(h1<=t1&&mt[i][q1[t1]]>=mt[i][j])--t1;
			while(h2<=t2&&mt[i][q2[t2]]<=mt[i][j])--t2;
			q1[++t1]=j,q2[++t2]=j;
		}
		_for(j,n,b){
			while(h1<=t1&&mt[i][q1[t1]]>=mt[i][j])--t1;
			while(q1[h1]<=j-n)++h1;
			q1[++t1]=j;
			ax[i][j]=mt[i][q1[h1]];
		}
		_for(j,n,b){
			while(h2<=t2&&mt[i][q2[t2]]<=mt[i][j])--t2;
			while(q2[h2]<=j-n)++h2;
			q2[++t2]=j;
			in[i][j]=mt[i][q2[h2]];
		}
	}
}
int main(){
	scanf("%d%d%d",&a,&b,&n);
	_for(i,1,a)_for(j,1,b)scanf("%d",&mt[i][j]);
	line();
	_for(i,n,a){
		_for(j,n,b){
			int mx=0,mn=inf;
			_for(k,i-n+1,i)mx=max(mx,ax[k][j]),mn=min(mn,in[k][j]),
			ans=min(ans,mx-mn);
		}
	}
	printf("%d\n",ans);
	return 0;
}

单调栈

一个具有单调性的栈。插入一个元素时,如果直接插入不满足单调性,就一直弹出,直到插入后满足单调为止。

Examples:

Blocks:luogu P3503/Loj P2453

题解在这里懒得复制了

luogu P4147

Meaning of the Problem

在一个 \(n*m\) 的矩阵中找到一个最大的矩形,求它的面积。

Solution

当遍历到第 \(i\) 行时,我们先算出它能向上延伸多少(即高度),然后把它的高度当作它所在矩阵的最低高度,用单调栈对他左右延伸,找出它所在矩形的宽度,即维护左边第一个比它矮的数的位置加 \(1\) 和右边第一个比它矮的数的位置减 \(1\) 。找出它的高度和宽度后,就可以计算出它的面积了。

Code

#include <bits/stdc++.h>
#define _for(i,a,b) for(int i=a;i<=b;++i)
#define for_(i,a,b) for(int i=a;i>=b;--i)
#define ll long long
using namespace std;
const int N=1010,inf=0x3f3f3f3f;
int n,m,a[N],ml[N],mr[N],ans;char c;
struct stru{int w=0,h=0;};
int main(){
	scanf("%d%d",&n,&m);
	_for(i,1,n){
		stack<stru>s1,s2;
		memset(ml,0,sizeof(ml)),memset(mr,0,sizeof(mr));
		_for(j,1,m){
			stru x;x.w=1;c='\0';
			while(c<'A'||c>'Z')c=getchar();
			if(c=='F')++a[j];
			else a[j]=0;
			while(a[j]&&!s1.empty()&&s1.top().h>=a[j])x.w+=s1.top().w,s1.pop();
			x.h=a[j];s1.push(x);ml[j]=x.w;
		}for_(j,m,1){
			stru x;x.w=1;
			while(a[j]&&!s2.empty()&&s2.top().h>=a[j])x.w+=s2.top().w,s2.pop();
			x.h=a[j];s2.push(x);mr[j]=x.w;
			ans=max(ans,a[j]*(mr[j]+ml[j]-1));
		}
	}
	printf("%d",ans*3);
	return 0;
}

\[\huge\mathfrak{The\ End} \]

posted @ 2022-01-21 22:03  K8He  阅读(104)  评论(0编辑  收藏  举报