单调栈指的是一个栈,栈内的元素是单调的。

单调递增栈:从栈底到栈顶元素依次从大到小

单调递减栈:从栈底到栈顶元素依次从小到大

建立一个单调栈

例:如果要维护一个递增单调栈(也就是栈顶的元素最小),那么元素要入栈时,如果为空栈或者栈顶元素比入栈元素大,就入栈,否则就依次将栈顶元素出栈,直到满足条件。

伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
stack<int> s1;
//一般需要在数组中添加一个结束标识符
for(遍历数组){
if(栈为空||栈顶元素大于当前元素){
入栈;
}else{
while(栈不为空&&栈顶元素小于当前元素){
栈顶元素出栈;
更新结果;
}
当前元素入栈;
}
}

应用

1.视野总和:

有n个人在排队,他们身高各不相同,都向右看,能看到比自己个子矮的人的头顶,直到下一个比自己更高的人。

输入:n个数表示n个人的身高输出:所有人能看到的头顶总和

例:输入: 4 3 7 1
输出:2
(个子为4的人能看见个子为3的人,7能看见1)

思路:本质就是找到下一个比自己个子高的人,然后计算相隔的距离(也就是中间有几个人),再加和。
可以通过构建单调递增栈来解决。当遇到下一个比自己高的人时就开始处理数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const int INT_INF = 0x3F3F3F3F;
int fieldSum(vector<int>& v){
v.push_back(INT_INF);
stack<int> st;
int sum = 0;
for(int i = 0;i<v.size();i++){
if(st.empty() || v[i]<st.top()){//如果空栈或者将要入栈的元素更小
st.push(v[i]);
}else {
while(!(st.empty() || v[i]<st.top())){
int top = st.top();
st.pop();
sum += (i - top - 1);//处理数据
}
st.push(v[i]);
}
}
return sum;
}

2.柱形图中的最大矩形面积

链接:https://ac.nowcoder.com/acm/contest/22669/Q

柱状图是有一些宽度相等的矩形下端对齐以后横向排列的图形,但是小A的柱状图却不是一个规范的柱状图,它的每个矩形下端的宽度可以是不相同的一些整数,分别为a[i]a[i]a[i],每个矩形的高度是h[i]h[i]h[i],现在小A只想知道,在这个图形里面包含的最大矩形面积是多少

输入描述:

1
2
3
一行一个整数N,表示长方形的个数
接下来一行N个整数表示每个长方形的宽度
接下来一行N个整数表示每个长方形的高度

输出描述:

1
一行一个整数,表示最大的矩形面积

输入

1
2
3
7
1 1 1 1 1 1 1
2 1 4 5 1 3 3

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;//pair int int(pii)
typedef long long int ll;
const int INF = 0x3f3f3f3f;//无限大
ll maxSqu(vector<pii> vec){
vec.push_back(make_pair(-1*INF,0));//在数组最后插入一个结束标识符,保证运行的时候能处理掉所有的数据
stack<pii> st;
ll max = -1*INF;//存储结果
for(int i = 0;i<vec.size();i++){//遍历数组
if(st.empty() || st.top().first<= vec[i].first){//如果符合条件就入栈
st.push(vec[i]);
}else{//不符合条件就依次出栈直到可以入栈
int wid = 0;//用来表示现在宽度是多少
while(!st.empty() && st.top().first > vec[i].first){//出栈
pii top = st.top();
st.pop();
wid+=top.second;
ll res = ll(wid)*top.first;
max = max>res?max:res;
}
//下面这两行很关键,用来实现向左拓展矩形(通过将栈顶元素的宽度改成它以及它左边比它高的矩形的宽度的和)
st.push(vec[i]);
st.top().second += wid;
}
}
return max;
}
int main(){
int n;
cin>>n;
vector<pii> v(n);
for(int i = 0;i<n;i++){
cin>>v[i].second;
}
for(int i = 0;i<n;i++){
cin>>v[i].first;
}
cout<<maxSqu(v);
return 0;
}