「学习笔记」单调队列与单调栈
「学习笔记」单调队列与单调栈
点击查看目录
单调队列
一个具有单调性的队列。插入一个元素时,如果直接插入不满足单调性,就一直从后弹出,直到插入后满足单调为止。一般用双端队列实现。
(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}
\]