最近我发现很多人不知道单调栈是什么,单调队列是什么,比如我的队友,所以我想统一讲一下单调栈和单调队列。但是即使我知道单调栈和单调队列,由于洛谷上面没有单调栈专题,我是没有练习过单调栈的,导致我在比赛中十分生疏,失误连连,最后痛失ICPC银牌,我因为这个几天都没睡好,像是得了银川+单调栈PTSD,一听到这两个词就难受。
单调栈和单调队列与其说是一种数据结构,我更认为他们是一种算法思想。
单调栈:维护一个保持栈的单调性
单调队列:维护一个队列的单调性
两者名字相似,作用完全不同
单调栈:找向左,向右第一个比当前元素小(大)的元素。O(n)算法。
单调队列:私以为单调队列就是滑动窗口,大部分是定长的滑动窗口,也有些变长的滑动窗口,那些变长的滑动窗口其实更像是尺取的一种思想。滑动窗口,顾名思义,就是在一个数组结构中滑动的窗口,作用,维护在数组中的一个定长的窗口的最大值(最小值),这里听不懂窗口可以直接看例题。O(n)算法。
GD是一个热衷于寻求美好事物的人,一天他拿到了一个美丽的序列。
为了研究这个序列的美丽程度,GD定义了一个序列的“美丽度”和“美丽系数”:对于这个序列的任意一个区间[l,r],这个区间的“美丽度”就是这个区间的长度与这个区间的最小值的乘积,而整个序列的“美丽系数”就是它的所有区间的“美丽度”的最大值。现在GD想要你帮忙计算这个序列的“美丽系数”。
第一行一个整数n,代表序列中的元素个数。 第二行n个整数a1、a2„an,描述这个序列。
一行一个整数,代表这个序列的“美丽系数”。
输入 #1
3 1 2 3
输出 #1
4
样例解释 选取区间[2,3],可以获得最大“美丽系数”为2*2=4。 数据范围 对于20%的数据,n<=2000; 对于60%的数据,n<=200000; 对于100%的数据,1<=n<=2000000,0<=ai<=2000000。 提示 你可能需要一个读入优化。
图是用win10的画图软件画的,图与题目样例无关,画的不好,凑合看。上面的题可以建模成这样的图,然后找一个最大矩形,大概就是红色或者黄色矩阵,在学习单调栈之前,我们做这种题只能用O(n^2)的方法,而离散处理的话,只要出题人卡一手离散数据就还是O(n^2)。
使用单调栈,我们只需向左向右分别维护一个单调栈,找出每个点的左端点和右端点,然后再一遍O(n)求出最大值max{a[i]*(R[i]-L[i])},a[i]是高度,R[i]是向右第一个比当前点高度小的点,L[i]是向左第一个比当前点高度小的点。本题的点不是连续的,是离散的,所以结果是max{a[i]*(R[i]-L[i]-1)}。
题解代码有很多,直接在洛谷里面就能看,我这边给出一个我的代码风格的单调栈模板,具体算法思想见代码及注释。
int L[maxn],R[maxn],a[maxn];//L-左边第一个比当前元素小的元素的位置,R同理
int S[maxn];//栈
int n;//数组大小
void monoStack(){
int top=0;//栈顶
for(int i=1;i<=n;i++){
while(top>0&&a[i]<=a[S[top]])top--;//弹出所有不小于当前数的数
L[i]=S[top];//记录左边第一个比这个数小的数,栈空就是0
S[++top]=i;//将当前位置入栈
}
top=0;
S[0]=n+1;
for(int i=n;i>=1;i--){//和上面同理,不写了
while(top>0&&a[i]<=a[S[top]])top--;
R[i]=S[top];
S[++top]=i;
}
}
现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
例如:
The array is [1 3 -1 -3 5 3 6 7], and k = 3.
输入一共有两行,第一行为n,k。
第二行为n个数( 输出共两行,第一行为每次窗口滑动的最小值 第二行为每次窗口滑动的最大值 输入 #1 输出 #1 50%的数据,n<=10^5 100%的数据,n<=10^6 这边给出一个我自己写习惯的单调队列,目前还没出过BUG,详细算法见代码及注释。最小值同理,稍微改一下出队条件即可。 输出格式
输入输出样例
8 3
1 3 -1 -3 5 3 6 7
-1 -3 -3 -3 3 3
3 3 5 5 6 7
说明/提示
思路:单调队列的作用就是维护一个序列的一个窗口的最小值(最大值),因此,本题是纯裸题。
int a[maxn];
int n,k;//n为数组长度,k为窗口长度
int getMax(){//fi和se被宏定义为first和second
deque