单调栈学习笔记

858 字
4 分钟
单调栈学习笔记

以洛谷 P5788 【模板】单调栈 为例。

题目描述 - 单调栈的基本作用#

题目很简单:给定一个数列,求每一个数往后看第一个比它大的数是谁。

我们来看样例:

5
1 4 2 3 5

显而易见,对于第一个数字 1 ,第一个比它大的数字是 4 ,排在第二位;对于第二个数字 4 ,第一个比它大的数字是 5 ,排在第五位……以此类推。

深入理解单调栈#

下面让我们把数据可视化。

首先迈出关键一步:因为题目中要求的是向右边看第一个比当前高的,也就是说已知信息应该在当前的右边。所以我们要以从右向左的顺序来寻找规律。否则,本题将难以解决。

我们先来看最后一个 5 。因为这是我们第一个看的,其右边没有更多信息了,所以按题目输出为 0

看完 5 ,往左一个看到 3 。由于 35 小,所以输出为 5 (这是真实 5 的位置)。同理, 2 的输出为 4 (这是真实 3 的位置)。

现在我们已经看了 2, 3, 5 ,也就是数字 4 的后面有 2, 3, 5。现在看到了数字 4 。我们发现 4 往后看一个是 2 ,然而 4>24 > 2 ,所以 2 不是我们要找的数,丢掉;丢掉了 2 ,看到 3 ,由于 4>34 > 3 ,所以 3 也不是要找的数,丢掉;终于看到了 5 ,由于 4<54 < 5 ,所以 5 就是我们要找的数了。

让我们来思考最后一个数字 1 。显然,它的对应数就是 4 。如果把 1 改成 7 呢?如图:

由于我们已经丢掉了 4 后面所有小于 4 的数字,所以这些被丢掉的数字一定也小于 7 ,我们不用操心这些数字里是否有我们可能要寻找的数字。那么,我们再次进行“丢掉”操作,现在 7 后面的数字有 4, 52, 3 已经被 4 丢掉了),而 4, 5 都小于 7 ,所以 4, 5 都被丢掉了,剩下了一个‘空’,所以 7 的答案变成了 0

让我们来重新审视一下整个过程:

  1. 从右往左看,如果后面的数字有比当前的数字小的,就丢掉;
  2. 丢到剩下面对的第一个比当前的大为止,这个数就是满足要求的。

这样一看,这似乎就是一个栈的操作,而且这个栈中的元素是单调递增或递减的。我们就将其称作单调栈

来看看 OI-wiki 上对单调栈的定义:

顾名思义,单调栈即满足单调性的栈结构。与单调队列相比,其只在一端进行进出。

代码实现#

读入完后,我们需要:

  1. 从右往左看:
for(int i=n;i>=1;i--)
  1. 弹出栈中所有小于等于当前元素的:
while(!s.empty()&&a[i]>=a[s.top()]){
s.pop();
}
  1. 获得当前答案:
ans[i]=(s.empty()?0:s.top());
  1. 将当前元素压入栈中:
s.push(i);

注意:输入输出应该使用 scanf ,否则会超时。

Full Code#

#include <bits/stdc++.h>
using namespace std;
const int maxn=3e6+10;
int n;
int a[maxn];
int ans[maxn];
stack <int> s;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=n;i>=1;i--){
while(!s.empty()&&a[i]>=a[s.top()]){
s.pop();
}
ans[i]=(s.empty()?0:s.top());
s.push(i);
}
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
}
return 0;
}

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
单调栈学习笔记
https://www.0x3f.foo/posts/daen-dio-zan/
作者
Dignite
发布于
2022-07-23
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Dignite
When nothing goes right, go left.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
站点统计
文章
146
分类
5
标签
271
总字数
314,753
运行时长
0
最后活动
0 天前

目录