直方图中最大的矩形
直方图是由在公共基线处对齐的一系列矩形组成的多边形。
矩形具有相等的宽度,但可以具有不同的高度。
例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1:
2559_1.jpg
通常,直方图用于表示离散分布,例如,文本中字符的频率。
现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。
图例右图显示了所描绘直方图的最大对齐矩形。
输入格式
输入包含几个测试用例。
每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目。
然后跟随n个整数h1,…,hn。
这些数字以从左到右的顺序表示直方图的各个矩形的高度。
每个矩形的宽度为1。
同行数字用空格隔开。
当输入用例为n=0时,结束输入,且该用例不用考虑。
输出格式
对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。
每个数据占一行。
请注意,此矩形必须在公共基线处对齐。
数据范围
1≤n≤100000,
0≤hi≤1000000000
输入样例:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
输出样例:
8
4000
首先分析本题,我们可以考虑任意一个直方图,
现在我们假定确定了这个最大面积的直方图的高度
那么这个最大面积的直方图一定是,从该位置一直向左移动到比这个高度小的这个距离
与向右到达比这个高度小的移动距离之和
当我们确定这个最大面积直方图的两个边界是比其中的那个高度小时
也就是说明在边界之内其他位置的高度都要大于等于那个高度
现在我们总结确定了的最大面积直方图的性质,
(1)高度有其中一个高度确定
(2)在该部分直方图内所有位置的高度都要大于等于那个确定的高度
(3)在这部分直方图左右边界扩展一位时,左右边界都要小于这个确定高度的直方图的高度
当然左边界小于其右边一位的高度,右边界小于其左边一位的高度
这样我们得到一个性质只要发现一个(向右方向)下降位置的出现那么在其之前进行扫描
直到发现一个(向左方向)的下降,那么该区间就有可能出现最大面积直方图
这种思想就是单调栈的思想,只有在一个单调递增的区间内有面积最大直方图
因为只要遇到单调下降,我们就停下来扫描之前的数据去寻找最大面积直方图
因此面积最大直方图一定是单调递增的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
LL q[N],w[N],h,n;//q数组用于存储每个位置的高度
//w数组用于存储以当前位置为高度向前能到达的最远的距离
//h是每次输入的临时变量
int main(){
while(cin>>n,n)
{
memset(q,-1,sizeof q);//每次将栈都重置
LL top=0;//设置栈顶指针元素
LL ans=0;//初始化本数据的最大直方图的面积
for(int i=1;i<=n+1;i++)
{//虽然外面只输入n个数据但只有n+1个数据才能
//有可能把前边n数据都遍历一遍
if(i!=n+1)
cin>>h;//这一步是输入每个位置的元素
else h=0;
//在确定了我们要入栈的数据后我们就要判断其
//是否符合单调递增的性质,在进而来判断是否需要向前遍历来寻找最大面积直方图
if(h>=q[top])//当要入栈的元素和之前的数据符合单调递增的数据时,直接入栈
q[++top]=h,w[top]=1;//因为符合单调递增性,因此向右的遍历宽度只有1也就是其本身
//当不满足单调递增性时,也就是到达一个单调递增区间的边界时
//我们向前查找最大面积直方图
else
{
LL cnt=0;//表示向左移动的距离
//因为遍历完之后w[top]要用到
while(h<q[top])//当其一直不满足单调递增性时,我们一直查找
{
ans=max(ans,(cnt+w[top])*q[top]);
//每次我们都假设当前位置是我们寻找的那个位置
//因为是单调递增的所以面积是移动的距离加上当前位置的宽度在乘以该位置高度
cnt=cnt+w[top--];
//更新移动的距离,即把当前位置加上去还要向前移动栈顶指针(即出栈)
}
//当循环结束时,表示行程单调递增栈,可以入栈
q[++top]=h,w[top]=cnt+1;
//当前位置可以向前到达的宽度应该是向前移动的距离在加上其本身的长度
}
cout<<ans<<endl;
}
}
return 0;
}