天天看点

A-最大矩形(单调栈)A-最大矩形(单调栈)

A-最大矩形(单调栈)

一、题目描述

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是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
           

二、思路与算法

首先,本题最核心的算法为单调栈的实现,通过两次单调栈,求得每个元素左边第一个小于它的元素编号、每个元素右边第一个小于它的元素编号。

用Left数组存储每个元素左边第一个小于它的元素编号。

用Right数组存储每个元素右边第一个小于它的元素编号。

接下来通过两次遍历,一次从0到n-1,一次从n-1到0,分别填充Right和Left。

以填充Right为例:

选用单调递增栈(s数组实现),当栈非空且大于等于栈顶元素时,入栈,否则弹出栈顶元素直到栈空或栈顶元素小于该元素。

弹出时注意,把元素A弹出去的元素B,B就是A右边第一个小于A的元素,以此规律,每次弹出元素的同时,把Right中该元素对应位置填充好。

最后,清空栈中元素。

如果所有元素都遍历结束,栈中还有元素,则这些元素右侧没有比它小的元素,所以把它们对应的Right位置填充为直方图末尾。

Left数组填充与Right数组填充非常相似。

最后,遍历直方图中每个高度,计算以某高度为矩形的高,最大宽度(Right对应值、Left对应值计算得到),得到的矩形面积,从所有高度对应矩形面积中选出最大的一个,即为结果。

三、代码实现

#include<cstdio> 
using namespace std;

int n=0;
int a[100010]={0};
int s[100010]={0};//单调栈
int Right[100010]={0};//存每个元素右侧第一个小于它的元素标号
int Left[100010]={0};//存每个元素左侧第一个小于它的元素标号

int main()
{
	scanf("%d",&n);
    while(n!=0)
    {
		int p=-1;
        long long maxSize=0;
        
        for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
		}
        for(int i=0;i<n;i++){
			while(p>=0&&a[i]<a[s[p]]){
				Right[s[p]]=i;//把某元素挤出栈的元素,就是它右边第一个比它小的元素 
				p--;//出栈 
			} 
			s[++p]=i;//入栈
		}
		while(p>=0)  //清空栈 
		{	Right[s[p]]=n;
			p--; 
		}
		p=-1;
		for(int i=n-1;i>=0;i--){		 
			while(p>=0&&a[i]<a[s[p]]){
				Left[s[p]]=i;	p--;
			}
			p++;
			s[p]=i;
		}
		while(p>=0)
			{Left[s[p]]=-1;	p--;} 
		for (int i=0;i<n;i++){
			if(maxSize<(long long)a[i]*(Right[i]-Left[i]-1)){
				maxSize=(long long)a[i]*(Right[i]-Left[i]-1);
			}
		}
        printf("%d\n",maxSize);
		scanf("%d",&n);
    }
    return 0;
}
           

四、经验与总结

  1. 注意!一直WA在于最大面积的计算方法处:

    maxSize=(long long)a[i]* (Right[i]-Left[i]-1);

    一定要先把a[i]转换为long long形式,然后再做乘法运算,运算中发生隐式转换,结果就是long long类型的,才不会出错!

    如果long long temp;

    temp=(long long)(a[i]*(Right[i]-Left[i]-1));

    则依然会出错,精度不够,造成WA!

    所以,切记运算如果可能超出精度时,一定要先对某个操作数进行形式转换!否则运算时精度已经损失掉了,再转换形式已经没用了。

  2. 可以更多地把数组等变量声明为全局变量,通过对题目中数据规模的掌握。
  3. 此题填充Right、Left数组时最容易忽略掉遍历完成后,清空栈的操作,需要注意。

继续阅读