一、题目描述
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是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
二、思路概述
1.把矩形的高存进数组p里面,然后从前往后,依次进单调递增栈;每次判断一个高的时候,如果必须弹出一个高,那么以这个高为高的矩形的最右边边界就是此时判断的高的编号;
2.判断最左边边界的方法,也是使用单调递增栈,但是是从后往前把数组元素压入栈,来找到左边界。
3.找到两个边界后就可计算可实现的矩形的面积,找出最大面积,即为所求答案。
三、细节
1.第一次出现了失误,是因为h的数据范围虽然在2e9的范围内,但是在计算面积sum的时候,面积有可能会超过2e9,所以面积要求是longlong型的。与此同时,计算sum的公式右边如果h和length都是int型,就会爆掉,无法强转成longlong。所以要求h和length至少一个是longlong型的。
long long area=0;
long long sum=0;
for(int i=0;i<n;i++)
{
sum=p[i].h*length[i];
if(area<sum)area=sum;
}
cout<<area<<endl;
2.感觉如果数据范围比较大的话,即使没有超出范围,还是尽量用longlong吧。
3.这一题比较麻烦的地方就是分析如何利用单调栈,来找出矩形使用一个高时,宽最大应该如何计算。
四、完整代码
#include<iostream>
#include<stack>
using namespace std;
struct point
{
long long h;
int num;
};
void area(point *p,int n)
{
stack<point> xp1;//单增栈,计算每个高度往右伸展
stack<point> xp2;
int *left=new int[n];
int *right=new int[n];
int *length=new int[n];
//计算右边界
for(int i=0;i<n;i++)
{
while(!xp1.empty() &&xp1.top().h>p[i].h)
{
point pp=xp1.top() ;
xp1.pop() ;
right[pp.num ]=i;
}
xp1.push(p[i]);
}
while(!xp1.empty() )
{
point pp=xp1.top() ;
xp1.pop() ;
right[pp.num ]=n;
}
//计算左边界
for(int i=n-1;i>=0;i--)
{
while(!xp2.empty() &&xp2.top().h>p[i].h)
{
point pp=xp2.top() ;
xp2.pop() ;
left[pp.num ]=i;
}
xp2.push(p[i]);
}
while(!xp2.empty() )
{
point pp=xp2.top() ;
xp2.pop() ;
left[pp.num ]=-1;
}
for(int i=0;i<n;i++)
{
length[i]=right[i]-left[i]-1;
}
long long area=0;
long long sum=0;
for(int i=0;i<n;i++)
{
sum=p[i].h*length[i];//谨慎,右边计算的时候可能会爆掉
if(area<sum)area=sum;
}
cout<<area<<endl;
}
int main()
{
int n;//n来表示直方图中小矩形的个数
while(cin>>n)
{
if(n==0)
break;
point *p=new point[n];//h表示直方图中从左到右每个小矩形的高度
for(int i=0;i<n;i++)
{
scanf("%lld",&p[i].h);//存储数据
p[i].num =i;
}
area(p,n);
}
return 0;
}