如果你已经知道什么是栈了,那么单调栈于你而言就像知道1+1=2后学习1+2=3一样非常简单
如果你还不知道什么是栈,那么我建议你可以先学习一下栈,你也可以选择看一下我的这一篇文章:堆栈的使用-CSDN博客
单调栈的定义
首先我们来看看单调栈是怎么定义的
一、单调递增栈:栈中的元素从栈顶到栈底是单调递增的,这样的栈就是单调递增栈
二、单调递减栈:栈中的元素从栈顶到栈底是单调递减的,这样的栈就是单调递减栈
怎么样,定义是不是很简单,其实他实现起来也一样简单
单调栈的实现
下面让我们来看看单调栈是如何实现的吧
单调递增栈的实现
根据他的定义,因为他需要从上到下单调递增,那么我们只需要在存当前元素的时候,让当前元素与栈顶元素进行比较,如果比栈顶元素下,那么直接压入;如果比栈顶元素大,那么弹出栈顶元素,再去与新的栈顶元素进行比较,如果还是比新的栈顶元素大,那么重复此步骤,直至把需要存入的元素存入为止。
单调递减栈的实现
单调递减栈的实现操作同单调递增栈几乎一模一样,只不过在比较大小的时候,应该是待存入元素比栈顶元素大才能压入,否则弹出栈顶元素再重复此步骤。再次我就不过多赘述了。
单调栈的运用
和前缀和之类的方法一样,单调栈有他适配的运用场景
当你需要去找当前元素向左 / 向右的比他大 / 小的第一个元素之类的问题时,便可以使用单调栈
例题
暴食之史莱姆(牛客 多校)
题目链接
K-暴食之史莱姆_河南萌新联赛2024第(三)场:河南大学 (nowcoder.com)
题目大意
给你n个史莱姆,再给你着n个史莱姆的体积大小,规定当且仅当一个史莱姆的体积大于等于他身旁的史莱姆时,他可以吃掉他身旁的那只史莱姆,但他吃完后,他的体积就会变为他所吃掉的史莱姆的体积。题目要求你求每只史莱姆最多可以吃多少只史莱姆。
解题思路
我们先这样去考虑,假设当前元素 x 左边最多可以吃 lx 个,右边最多可以吃 rx个。那么其实我们能吃的总数一定是 lx+rx ,我们并不用担心x 吃完左边或者右边的某个元素后不能去吃完同侧剩下的和另一侧剩下的史莱姆,因为我们可以证明一定能找到某种顺序,使得着 lx+rx 个 史莱姆都被吃掉。
所以现在问题就变成了当前元素 x 左边最多可以吃 lx 个,右边最多可以吃 rx个。既是他左边第一个比他小的史莱姆素能吃多少个史莱姆+1 和 右边第一个比他小的史莱姆能吃多少个史莱姆+1。既是单调栈问题了
代码如下
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<stack>
using namespace std;
typedef long long LL;
struct nuu
{
int xiabiao;
int houc;
int zhi;
};
stack<nuu> st1;
stack<nuu> st2;
int hou[200010];
int qian[200010];
int main()
{
int n;
scanf("%d", &n);
int a[n];
for(int i=0;i<n;i++)
scanf("%d", &a[i]);
//hou[n-1]=0;
//st.push({n-1, hou[n-1], a[n-1]});
for(int i=n-1;i>=0;i--)
{
f:
if(!st1.empty())
{
if(a[i]>=st1.top().zhi)
{
hou[i]=st1.top().houc+1;
st1.push({i, hou[i], a[i]});
}
else
{
st1.pop();
goto f;
}
}
else
{
hou[i]=0;
st1.push({i, hou[i], a[i]});
}
}
for(int i=0;i<n;i++)
{
t:
if(!st2.empty())
{
if(a[i]>=st2.top().zhi)
{
qian[i]=st2.top().houc+1;
st2.push({i, qian[i], a[i]});
}
else
{
st2.pop();
goto t;
}
}
else
{
qian[i]=0;
st2.push({i, qian[i], a[i]});
}
}
for(int i=0;i<n;i++)
{
printf("%d ", qian[i]+hou[i]);
}
return 0;
}