题目:
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是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
题目分析:
要求直方图中的最大矩形的面积,根据矩形面积公式在已知高度的前提下,必须要求出矩形的宽度,而在直方图中 矩形高度以[l,r]区间最小高度为准,矩形宽度=l-r+1。所以该题关键在于如何确定适合的l,r作为区间。
-
要使整个矩形最大,那么高度就确定为l到r中的最小值。
-
同理,如果确定了矩形的高度,那么左端点一定是越靠左, 右端点越靠右,这个矩形的面积才可能最大。
-
左端点可以确定为往左数第一个小于此高度的点。
-
右端点可以确定为往右数第一个小于此高度的点。
-
单调栈中栈内元素自栈顶到栈底满足单调性。
• 单调递增栈 可以找到往左/往右第一个比当前元素 大 的 元素 。
• 单调递减栈 可以找到往左/往右第一个比当前元素 小 的 元素 。
分别用单调递增栈和单调递减栈求出以每个点为高时的左右端点。
wa了很多次,原因在于数据类型,题给高度取值范围 0 <= hi <= 1000000000,虽然hi的范围不会超出int范围,但是在求取面积的过程中h和(l-r+1)相乘爆了int范围,所以会wa,所以需要将运算中的其中一个强转为long long类型的,在这里我将高度和面积都转换成了long long型的。嘤 long long 对应lld哦,别忘了(我就是忘了又多wa了一次 唉)
代码:
#include<iostream>
#include<stdio.h>
#include<stack>
using namespace std;
const int maxn=1e5+100;
long long L[maxn],R[maxn],w[maxn],a[maxn];
int n;
void solve()//找出各点右端点
{
stack<int> sa;
int l=1;int r=0;
for(int i=1;i<=n;i++)
{
while(sa.size()>0 &&a[sa.top()]>a[i] )//单调递减栈找出右边第一个比当前元素小的元素
{
R[sa.top()]=i-1;
sa.pop();
}
sa.push(i);
}
while(sa.size()>0)//单调栈里的元素赋区间最右端点
{
R[sa.top()]=n;
sa.pop();
}
}
void solve1()//找出各点左端点
{
stack<int> sa;
int l=1;int r=0;
//while(n--)
for(int i=n;i>=1;i--)
{
while(sa.size()>0 &&a[sa.top()]>a[i] )//单调递减栈找出左边第一个比当前元素小的元素
{
L[sa.top()]=i+1;
sa.pop();
}
sa.push(i);
}
while(sa.size()>0)//单调栈里的元素赋区间最左端点
{
L[sa.top()]=1;
sa.pop();
}
}
int main()
{
//int n;//直方图小矩形个数
while(scanf("%d",&n)!=EOF)
{
if(n==0)
{
return 0;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
solve();
solve1();
long long ans=0;
for(int i=1;i<=n;i++)//计算面积
{
w[i]=a[i]*(R[i]-L[i]+1);
}
for(int i=1;i<=n;i++)//找出最大面积
{
if(w[i]>ans)
{
ans=w[i];
}
}
printf("%lld\n",ans);
}
return 0;
}