关于单调栈的简单介绍请看这篇博客:https://blog.csdn.net/liujian20150808/article/details/50752861
总结:
一个元素向左遍历的第一个比它小的数的位置就是将它插入单调栈时栈顶元素的值,若栈为空,则说明不存在这么一个数。然后将此元素的下标存入栈,就能类似迭代般地求解后面的元素
模板:
stack<int >S;
for(int i=1;i<=n;i++){
while(S.size()&&a[S.top()]>=a[i]) S.pop();
if(S.empty()) L[i]=0;
else L[i]=S.top();
S.push(i);
}
例题:HDU1506
Largest Rectangle in a Histogram
http://acm.hdu.edu.cn/showproblem.php?pid=1506
首先考虑最大面积的矩形X的左右边界的性质:
设其左边界为L,右边界为R,则其高H = min{h[i] | L <= i <= R}
此时最大面积为 (R - L + 1) * H
若此时左边界的左边那个矩形的高度 h[L-1] >= H
则左边界可以向左拓展,则新的面积为:
(R - (L-1) + 1) * H > 原面积
则与原假设条件冲突
故左边界左边的那个矩形的高度 :h[L-1] < H
同理右边界右边的那个矩形的高度: h[R+1] < H
设H = h[i]
所以左边界L是满足h[j-1] < h[i]的最大的j,即从i点向左遍历的第一个高度比i小的点的右边一个点
而右边界R是满足 h[j+1] < h[i]的最小的j,即从i点向右遍历第一个高度比i小的点的左边一个点
所以我们可以利用单调栈的性质得到每个确定点,即确定高度的最大面积矩形的左右边界,然后枚举取最大即可。
#include <bits/stdc++.h>
using namespace std;
const int N=100000+100;
typedef long long ll;
stack<int>S;
ll h[N];
int R[N],L[N];
int main(){
int n;
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;i++) scanf("%lld",&h[i]);
while(S.size()) S.pop();
for(int i=0;i<n;i++){
while(S.size()&&h[S.top()]>=h[i]) S.pop();
if(S.empty()) L[i]=0;
else L[i]=S.top()+1;
S.push(i);
}
while(S.size()) S.pop();
for(int i=n-1;i>=0;i--){
while(S.size()&&h[S.top()]>=h[i]) S.pop();
if(S.empty()) R[i]=n;
else R[i]=S.top();
S.push(i);
}
ll ans=0;
for(int i=0;i<n;i++){
ans=max(ans,h[i]*(R[i]-L[i]));
}
printf("%lld\n",ans);
}
return 0;
}
例题:http://poj.org/problem?id=3250
Bad Hair Day
很裸的单调栈,而且只需要跑一个方向。
#include <iostream>
#include <algorithm>
#include <stack>
#include <stdio.h>
using namespace std;
const int N=100000+100;
typedef long long ll;
stack<int>S;
ll h[N];
int R[N],L[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
scanf("%lld",&h[i]);
}
h[n]=0x3f3f3f3f;
for(int i=n;i>=0;i--){
while(S.size()&&h[S.top()]<h[i]) S.pop();
if(S.empty()) R[i]=0;
else R[i]=S.top()-i-1;
S.push(i);
}
ll num=0;
for(int i=0;i<n;i++){
// cout<<R[i]<<" ";
if(R[i]!=0) num+=R[i];
}
cout<<num<<endl;
}
例题:http://poj.org/problem?id=2796
Feel Good
经典单调栈
#include <iostream>
#include <algorithm>
#include <stack>
#include <stdio.h>
#include <cstring>
using namespace std;
const int N=100000+100;
typedef long long ll;
stack<int>S;
ll h[N];
ll R[N],L[N],Sum[N];
int main(){
int n,t=0;
scanf("%d",&n);
Sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%lld",&h[i]);
Sum[i]=Sum[i-1]+h[i];
}
while(S.size()) S.pop();
for(int i=1;i<=n;i++){
while(S.size()&&h[S.top()]>=h[i]) S.pop();
if(S.empty()) L[i]=0;
else L[i]=S.top();
S.push(i);
}
while(S.size()) S.pop();
for(int i=n;i>=1;i--){
while(S.size()&&h[S.top()]>=h[i]) S.pop();
if(S.empty()) R[i]=n;
else R[i]=S.top()-1;
S.push(i);
}
//for(int i=0;i<n;i++) printf("%d ",L[i]);
//printf("\n");
// for(int i=0;i<n;i++) printf("%d ",R[i]);
//printf("\n");
ll maxs=-1,rr,ll,r,l;
for(int i=1;i<=n;i++){
if(maxs<h[i]*(Sum[R[i]]-Sum[L[i]])){
rr=R[i];
ll=L[i]+1;
maxs=h[i]*(Sum[R[i]]-Sum[L[i]]);
}
}
printf("%lld\n%lld %lld\n",maxs,ll,rr);
return 0;
}