前言
只是了解一下单调栈的解决问题的思路
一、单调栈模板
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll ans=0;
stack<ll>p;
signed main()
{
IOS;
ll t;
cin>>t;
while(t--)
{
ll x;
cin>>x;
while(!p.empty()&&p.top()<=x)//这是一个单调递减栈,只需要把改为>=就相当于单调递增栈
p.pop();
ans+=p.size();
p.push(x);
}
cout<<ans<<endl;
return 0;
}
二、单调栈利用的场景
1.寻找下一个更大/小元素问题
例题:传送门:P2866 [USACO06NOV] Bad Hair Day S
解决代码
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll ans=0; // 存储最终结果,记录所有元素左侧比它大的元素数量之和
stack<ll> p; // 单调递减栈,用于维护元素左侧的较大值
signed main()
{
IOS;
ll t;
cin>>t; // 读取元素总数
// 遍历每个元素
while(t--)
{
ll x;
cin>>x; // 读取当前元素
// 步骤1:维护单调递减栈
// 弹出所有小于等于当前元素x的栈顶元素
// 因为这些元素不可能成为后续元素的"左侧第一个比它大的元素"
while(!p.empty() && p.top() <= x)
p.pop();
// 步骤2:计算贡献值
// 此时栈中的所有元素都大于当前元素x
// 栈的大小即为当前元素左侧比它大的元素数量
ans += p.size();
// 步骤3:将当前元素压入栈中
// 保持栈的单调性(从栈底到栈顶元素递减)
p.push(x);
}
cout<<ans<<endl; // 输出所有元素左侧比它大的元素数量之和
return 0;
}
主要思路:
2.柱状图相关问题
a.最大矩形面积
题目传送门: 柱状图中最大的矩阵
解决的完整代码
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
const ll N=1e6+10;
ll a[N];
signed main()
{
IOS;
ll n;
cin>>n;
stack<pii> p; // 单调递增栈,存储{高度, 宽度}
ll ans=0; // 最大矩形面积
ll wide=0; // 累积宽度,记录弹出元素的总宽度
// 读取所有柱子高度
for(ll i=1;i<=n;i++)
{
cin>>a[i];
}
// 添加哨兵元素,确保所有元素都会被弹出
a[n+1]=0;
// 遍历每个柱子(包括哨兵)
for(ll i=1;i<=n+1;i++)
{
wide=0; // 每次处理新柱子前重置累积宽度
// 弹出所有比当前柱子高的栈顶元素,并计算面积
while(!p.empty() && p.top().first > a[i])
{
wide += p.top().second; // 累积弹出元素的宽度
ans = max(ans, wide * p.top().first); // 更新最大面积
p.pop(); // 弹出栈顶元素
}
// 将当前柱子压入栈,宽度为累积宽度+1
p.push({a[i], wide+1});
}
cout<<ans<<endl; // 输出最大面积
return 0;
}
力扣的代码
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<pair<int,int>>p;
int ans=0;
int wide=0;
vector<int> a=heights;
a.push_back(0);
for(int i=0;i<a.size();i++)
{
wide=0;
while(!p.empty()&&p.top().first>a[i])
{
wide+=p.top().second;
ans=max(ans,wide*p.top().first);
p.pop();
}
p.push({a[i],wide+1});
}
return ans;
}
};
主要思路就是
b.接雨水问题
题目传送门: 接雨水
力扣代码:
class Solution {
public:
int trap(vector<int>& height) {
vector<int> a = height; // 复制高度数组便于操作
int len = a.size(); // 数组长度
stack<int> p; // 单调递减栈,存储数组下标
int ans = 0; // 记录总雨水量
int top1 = 0; // 临时存储弹出的栈顶下标
for (int i = 0; i < len; i++) {
// 当当前柱子高度大于栈顶柱子高度时,形成凹槽结构
while (!p.empty() && a[i] > a[p.top()]) {
top1 = p.top(); // 弹出栈顶元素,作为凹槽底部
p.pop();
// 若栈为空,说明无左边界,无法形成凹槽,跳过后续计算
if (p.empty())
break;
int l = p.top(); // 新的栈顶为左边界下标
int wide = i - l - 1; // 凹槽宽度:右边界-左边界-1
int hight = min(a[i], a[l]) - a[top1]; // 凹槽高度:左右边界较小值-底部高度
// 累加当前凹槽的雨水量
ans += hight * wide;
}
p.push(i); // 压入当前下标,维护单调递减栈
}
return ans; // 返回总雨水量
}
};
对于这一问题主要求的是可以形成的凹面积,主要利用的是单调递减栈
模拟过程
具体的演示步骤可以移步到 官方视频详解