1、单调栈的定义
单调栈,顾名思义,就是具有单调性的栈。因此,它依旧是一个栈结构,只不过里面存储的数据是严格递增或者严格递减的。这种结构是很容易实现的(如下面代码)
cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
// 维护一个单调递增的栈
void test1()
{
stack<int> st;
for(int i = 1; i <= n; i++)
{
// 栈里面大于等于a[i]的元素全部出栈
while(st.size() && st.top() >= a[i])
st.pop();
st.push(a[i]);
}
}
// 维护一个单调递减的栈
void test2()
{
stack<int> st;
for(int i = 1; i <= n; i++)
{
// 栈里面小于等于a[i]的元素全部出栈
while(st.size() && st.top() <= a[i])
st.pop();
st.push(a[i]);
}
}
那么,维护一个单调栈的意义是什么?
2、单调栈解决的问题
单调栈能帮我们解决下面四个问题:
·寻找当前元素左侧,离它最近,并且比它大的元素在哪;
·寻找当前元素左侧,离它最近,并且比它小的元素在哪;
·寻找当前元素右侧,离它最近,并且比它大的元素在哪;
·寻找当前元素右侧,离它最近,并且比它小的元素在哪。
虽然是四个问题,但是原理是一致的。所以,只要解决一个,举一反三就可以解决剩下的几个。
2.1 寻找当前元素左侧,离它最近,并且比它大的元素在哪

从左往右 遍历元素,构造一个单调递减的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素大的数;如果栈非空,插入当前位置元素时的栈顶元素就是所要找的元素。
注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。
cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
int ret[N];
void test()
{
stack<int> st;
for(int i = 1; i <= n; i++)
{
while(st.size() && a[st.top()] <= a[i])
{
st.pop();
}
if(st.size())
{
ret[i] = st.top();
}
st.push(i);
}
}
2.2 寻找当前元素左侧,离它最近,并且比它小的元素在哪

从左往右遍历元素,构造一个单调递增的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素小的数;如果栈非空,那么栈顶元素就是要找的元素。
注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。
cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
int ret[N];
void test()
{
stack<int> st;
for(int i = 1; i <= n; i++)
{
while(st.size() && a[st.top()] >= a[i])
{
st.pop();
}
if(st.size())
{
ret[i] = st.top();
}
st.push(i);
}
}
剩下两种情况原理类似,只需要修改一下遍历顺序即可。
2.3 寻找当前元素右侧,离它最近,并且比它大的元素在哪
cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
int ret[N];
void test()
{
stack<int> st;
for(int i = n; i >= 1; i--)
{
while(st.size() && a[st.top()] <= a[i])
{
st.pop();
}
if(st.size())
{
ret[i] = st.top();
}
st.push(i);
}
}
2.4 寻找当前元素右侧,离它最近,并且比它小的元素在哪
cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 3e6 + 10;
int a[N], n;
int ret[N];
void test()
{
stack<int> st;
for(int i = n; i >= 1; i--)
{
while(st.size() && a[st.top()] >= a[i])
{
st.pop();
}
if(st.size())
{
ret[i] = st.top();
}
st.push(i);
}
}
3、单调栈的应用

cpp
#include <iostream>
#include <stack>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
int n;
LL h[N], v[N];
LL sum[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> h[i] >> v[i];
}
stack<int> st1;
for(int i = 1; i <= n; i++)
{
while(st1.size() && h[st1.top()] <= h[i])
{
st1.pop();
}
if(st1.size())
{
sum[st1.top()] += v[i];
}
st1.push(i);
}
stack<int> st2;
for(int i = n; i >= 1; i--)
{
while(st2.size() && h[st2.top()] <= h[i])
{
st2.pop();
}
if(st2.size())
{
sum[st2.top()] += v[i];
}
st2.push(i);
}
LL ret = 0;
for(int i = 1; i <= n; i++)
{
ret = max(ret, sum[i]);
}
cout << ret << endl;
return 0;
}