单调栈
简单定义:单调栈就是从栈顶到栈底,元素满足单调性;
如单调递增栈:
如何维护单调栈?
以维护单调递增栈为例,当一个元素a入栈的时候,判断a和栈顶元素的大小,如果栈顶元素小于等于a,那么就弹出栈顶元素,直到栈顶元素大于a(或者栈为空)。
例如,把7放入栈当中:
单调栈的用处
*可以判断一个数组中,任何一个元素左边(右边)第一个比自己大(小)的元素。*比如上面的例子,如果数组是10 8 6 7;根据我们单调递增栈的维护,7左边第一个比自己大的元素是7(6比自己小在自己入栈的时候被弹出去了)
一个题目
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
小反思:这个题目里面小矩阵个数范围:1-100000;
矩阵高度范围:0 <= hi <= 1000000000
所以矩阵面积int可能会溢出,所以要用long long int
思路:
遍历直方图的每一个小矩形,找到它左边一个小于它的矩阵编号,再找到右边第一个小于它的矩阵编号,就可以找到它左右两边的边界,算出最大的长。
要实现这个功能就可以用上面的单调栈,维护单调递减栈,每放入一个矩形高度的时候,就可以找到它左边第一个小于它的元素下标。然后逆着放入矩阵高度数组,就可以找到右边第一个小于它的元素下标。
全部代码
#include<stdio.h>
#include<stack>
using namespace std;
long long int n,a[100000],l[100000],r[100000];
struct P
{
long long int value;long long int xiabiao;
};
stack<P> ST;
void fun1()//维护单调递减栈
{
while(!ST.empty()) ST.pop();
l[0]=-1;
P p;p.value=a[0];p.xiabiao=0;
ST.push(p);
for(int i=1;i<n;i++)
{
while(!ST.empty()&&a[i]<=ST.top().value)
{
ST.pop();
}
if(ST.empty())//如果是空,则左边的都比它大
l[i]=-1;
else l[i]=ST.top().xiabiao;
P now;now.value=a[i];now.xiabiao=i;
ST.push(now);
}
}
void fun2()//维护单调递减栈
{
while(!ST.empty()) ST.pop();
r[n-1]=n;
P p;p.value=a[n-1];p.xiabiao=n-1;
ST.push(p);
for(int i=n-2;i>=0;i--)
{
while(!ST.empty()&&a[i]<=ST.top().value)
{
ST.pop();
}
if(ST.empty())//如果是空,则左边的都比它大
r[i]=n;
else r[i]=ST.top().xiabiao;
P now;now.value=a[i];now.xiabiao=i;
ST.push(now);
}
}
int main()
{
while(scanf("%lld",&n))
{
long long int sum=0;
if(n==0) break;
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);//输入直方图
//对于每个小矩形,向左找第一个小于它的,向右找第一个小于它的
//维护单调递减栈
fun1();
fun2();
for(int i=0;i<n;i++)
{
long long int result=((r[i]-l[i])-1)*a[i];
if(result>sum) sum=result;
}
printf("%lld\n",sum);
}
return 0;
}