单调栈

1|0概述

栈中元素满足单调性的线性数据结构,单调栈一般维护的是一个数前/后第一个大于/小于他的数。

  1. 单调栈解决的主要问题是什么呢?

​ 就跟单调队列差不多。单调队列主要处理的是一个区间内的最大/小值,而单调栈处理的是寻找以某个值为最小/大值的最大区间。相比较,实际上单调栈用的虽然少一些,但是比单调队列更加灵活多变。

  1. 为什么单调栈是正确的呢?

​ 对于这道题来说,我们定义一个元素失效,当且仅当这个元素的 fi 被保存。我们假设将栈中已有元素。既然栈中元素没有被弹出,那么证明还没有遇到比它大的元素。当我们的元素从栈中弹出的时候,这证明了它发现了第一个比它还要大的数,这道题刚好满足,于是保存 fi ,继续算法流程。同理,对于之前的主要问题,我们找到了一个比它还要大的数,说明这个区间结束了。

  1. 单调栈的时间复杂度是?

​ 想一下,我们的每一个元素最多进栈/出栈一次,所以说时间复杂度 O(n)。

  1. 结束标识符

    对于某些特殊题目,栈内元素出不完,会导致答案错误,这时候我们要添加结束标识符强制吐出所有栈内元素。

template <class T, class Cmp = std::greater_equal<T>> // >= class MonotonyStack : private std::stack<std::pair<T, int>, std::vector<std::pair<T, int>>> { static constexpr Cmp cmp = Cmp(); public: MonotonyStack(const T &val, int id) {//一开始要放入的极限元素 this->emplace(val, id); } int push(const T &val, int id) { while (this->empty() == false and cmp(this->top().first, val)) { this->pop(); } assert(this->empty() == false); int result = this->top().second; this->emplace(val, id); return result; } };

2|0板题

P5788 【模板】单调栈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

https://www.luogu.com.cn/problem/solution/P5788

1|0题意

即求每一个数的下一个更大数的下标。

1|0思路

  • 维护一个单调递减的栈,即栈顶永远是大于等于目前元素的最小值
  • 每次插入元素与栈顶进行比较
    • 大于等于栈顶
      • 栈顶不停出栈,直到该元素小于栈顶或栈为空。
      • 出栈的每一个栈顶,对应的答案都是该元素。
    • 小于栈顶
      • 入栈

1|0时间复杂度

从出栈入栈的角度切入分析,所有元素都只会出栈一次,入栈一次,所以复杂度就是 O(n)

1|0代码

template <class T, class Cmp = std::greater_equal<T>> // >= class MonotonyStack : private std::stack<std::pair<T, int>, std::vector<std::pair<T, int>>> { static constexpr Cmp cmp = Cmp(); public: MonotonyStack(const T &val, int id) { this->emplace(val, id); } int push(const T &val, int id) { while (this->empty() == false and cmp(this->top().first, val)) { this->pop(); } assert(this->empty() == false); int result = this->top().second; this->emplace(val, id); return result; } }; void solve() { // #define tests int n; std::cin >> n; std::vector<int> a(n); for (auto& ai : a) {std::cin >> ai;} MonotonyStack<int, std::less_equal<int>> monostack(1E9, -1); std::vector<int> res(n); for (int i = n - 1; i >= 0; i--) { res[i] = monostack.push(a[i], i); } for (auto& x : res) {std::cout << x + 1 << ' ';} std::cout << '\n'; }

3|0最大矩形面积

D-Largest Rectangle in a Histogram_0x11 基本数据结构-栈 (nowcoder.com)

1|0思路

考虑维护每一个元素 i 左端第一个比它小的下标 li 及右端第一个比它小的下标 ri

显然可能的面积就是 ai×(rili1)

1|0代码

#include <bits/stdc++.h> using namespace std; int main() { int n; while(cin >> n && n) { vector<int> a(n + 2); vector<int> l(n + 2), r(n + 2); for (int i = 1; i <= n; i++) cin >> a[i]; a[n + 1] = 0; long long ans = 0; stack<int> st; for (int i = 1; i <= n; i++) { while(!st.empty() && a[i] <= a[st.top()]) { st.pop(); } if (!st.empty()) l[i] = st.top(); else l[i] = 0; st.push(i); } while(!st.empty()) st.pop(); for (int i = n; i; i--) { while(!st.empty() && a[i] <= a[st.top()]) { st.pop(); } if (!st.empty()) r[i] = st.top(); else r[i] = n + 1; st.push(i); } for (int i = 1; i <= n; i++) { ans = max(ans, 1LL * a[i] * (r[i] - l[i] - 1)); } cout << ans << endl; } return 0; }

4|0CF1730C

Minimum Notation - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

1|0题意

给定一个仅包含 09 这几个数字的字符串,你可以执行以下操作任意次:

选择字符串中的一个数字 d,将 d 删除后,向字符串任意位置插入一个数字 min(d+1,9)

求能够得到的字典序最小的字符串。

1|0思路

想要让字典序最小,就要尽可能将小的数放在前面。每次执行一次操作后,我们都会让一个数变大,只有变大后把它放在字符串后面,让一个更小的数能到前面来,他才能更优。

维护一个单调栈,将弹出的元素 d=min(d+1,9) 全部存入一个 vector 中,将所有元素排序。最后输出答案,在输出单调栈中元素的同时,输出 vector 中与之相等的元素,因为已经排完序了,直接从前向后扫即可。

void solve() { string s; stack<char> st; vector<char> els,ans; cin >> s; int len = s.length(); for(int i = 0;i < len;i++) { while(!st.empty() && st.top() > s[i]) { els.push_back(min((char)(st.top() + 1),'9')); st.pop(); } st.push(s[i]); } while(!st.empty()) { ans.push_back(st.top()); st.pop(); } sort(els.begin(),els.end()); int pos = 0; for(int i = ans.size() - 1;i >= 0;i--) { cout << ans[i]; while(pos < els.size() && els[pos] >= ans[i] && els[pos] <= ans[i-1] && i > 0) cout << els[pos++]; } for(int i = pos;i < els.size();i++) cout << els[i]; cout << endl; return; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17976128.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示