题目描述
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
输入输出
Input
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000.
然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000.
这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
Output
对于每组测试数据输出一行一个整数表示答案。
Sample Input
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample Output
8
4000
思路分析
该题用到单调栈的数据结构。单调栈既有单调的性质,又满足栈的特点。单调包括单调递增、单调递减、单调非减、单调非增。栈支持push和pop操作,满足先进后出。
对于该题目,我们需要遍历每个矩形,找到以它为高可以向左右扩展的最大长度
,求出面积,然后找到最大面积。那么就要考虑能以当前矩形的高为整个矩形的高的条件是什么,也就是向左或向右的矩形的高都大于等于当前这个矩形,即我们向左或向右找到第一个比当前矩形的高矮的矩形就停止寻找
。
而单调栈正有这样的功能,单调递减栈可以找到向左/向右第一个比当前元素小的元素。因此可以找到以当前元素为最值的最大区间。
注意
1.求面积之后ans会超过int的范围,因此定义时设为long long型。虽然定义的数组a[] R[] L[] st[]均未超int范围,但是求面积时要进行强制类型转换,所以干脆也直接定义成long long型。
2.当前元素与栈顶元素比较时选">“还是”<"依据单调性判断,是否有”=“依据单调递增(减)还是单调非减(增)来判断。
代码
#include<stdio.h>
using namespace std;
const int maxx=1e5+100;
long long a[maxx],R[maxx],L[maxx],st[maxx];
int n;
void findRight()
{//对每个i,向右寻找第一个比它小的位置
int top=0;//r是栈顶
for(int i=1;i<=n;i++)
{//注意 a[st[top]]>a[i]有没有等号,依据题目而定
while(top>0&&a[st[top]]>a[i]){//top是>0还是>=0要注意
R[st[top]]=i-1;
top--;//因为栈里弹出一个元素,所以栈顶指针减一
}
st[++top]=i;
}
while(top>0){//弹出剩余元素
R[st[top]]=n;
top--;
}
}
void findLeft()
{//对每个i,向右寻找第一个比它小的位置
int top=0;
for(int i=n;i>=1;i--)
{
while(top>0&&a[st[top]]>a[i]){
L[st[top]]=i+1;//注意是i+1,与向右寻找不同
top--;
}
st[++top]=i;
}
while(top>0){
L[st[top]]=1;
top--;
}
}
int main()
{
while(true){
scanf("%d",&n);
if(n==0) break;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
findRight();
findLeft();
long long ans=0,tmp=0;
for(int i=1;i<=n;i++)
{
//cout<<R[i]<<" "<<L[i]<<endl;
tmp=a[i]*(R[i]-L[i]+1);
if(tmp>ans)
ans=tmp;
}
printf("%lld\n",ans);//printf不取地址,人傻了
}
return 0;
}