最大矩形(单调栈)

在这里先简单描述一下单调栈
单调栈:一种线性数据结构,栈内元素自栈顶到栈底满足单调性。有单调递增栈和单调递减栈。如果要加入栈的元素不满足单调性,则要将栈顶元素弹出,直到满足条件为止,然后将该元素入栈
作用:

  • 复杂度是线性的
  • 单调递增栈可以找到往左/右第一个比当前元素大的元素
  • 单调递减栈可以找到往左/右第一个比当前元素小的元素
  • 可以求得以当前元素为最值的最大区间

问题描述:

给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
在这里插入图片描述

input:
输入包含多组数据。每组数据用一个整数 n n 来表示直方图中小矩形的个数 , , 你可以假定 1 n 100000 1\leq n\leq100000 。 然后接下来 n n 个整数 h 1 , . . . h n , h_{1},...h_{n}, 满足 0 h n 1000000000 0\leq h_{n}\leq1000000000 。 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为 1 1 。 测试数据以 0 0 结尾。
output:
对于每组测试数据输出一行一个整数表示答案。

样例输入:

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

样例输出:

8
4000

解题思路:

现在,每一个矩形的高都是已知的,但是矩阵的宽还不清楚。如果要求最大的矩形面积,就要已知矩形的高和宽,则在矩形高已知的情况下,要使矩形面积最大,肯定宽越大,即左端点越靠左,右端点越靠右,矩形的面积才可能最大。那左端点可以确定为往左数第一个小于此高度的点,右端点可以确定为往右数第一个小于此高度的点。

利用这个特点,我们可以使用两次单调递减栈,第一次栈找到第一个小于此高度的右端点,第二次栈找到第一个小于此高度的左端点。

接下来我们考虑实现,这里我们并没有使用stl中的stack,而是自己手写模拟栈,我认为这样更方便些。栈用一个数组表示,用一个top表示栈顶指针,top始终指向栈顶的位置。栈中的每一个元素都是一个结构体类型,有四个域,存储它的值,它的数组下标,它的左端点和右端点的值。因为单调栈的复杂度是线性的,我们使用一次for循环就可以完成对一个端点的查找。在求一个端点时,首先考虑要被放入到栈的元素是否小于栈顶元素,若小于,则栈顶元素弹出,并且修改栈顶元素的右端点的值为被放入栈的元素的下标,重复以上步骤,直到大于为止,在一次for循环结束后,栈中剩余元素的右端点就是最又端了。对于求左端点,只不过是将数组逆序再调用一次递归栈,原理类似。

解题总结:

如果该题目能够想到使用单调栈就十分容易了,但是要注意一点,如果变量类型使用int的话会爆精度,所以一定要使用long long!!

代码:

#include<iostream>
using namespace std;
struct limit{
	long long int low;  //左端点
	long long int high; //右端点
	long long int element;
	long long int location;  //数组下标
	limit & operator = (const limit &l)
	{
		low=l.low;
		high=l.high;
		element=l.element;
		location=l.location;
		return *this; 
	} 
	limit()
	{
		low=0;
		high=0;
		element=0;
		location=0;
	}
};
int main()
{
	long long int n;
	cin>>n;
	while(n)
	{
		limit *p=new limit [n+1];
		for(int i=1;i<=n;i++)
		{
			long long int temp;
			cin>>temp;
			p[i].element=temp;
			p[i].location=i;
		}
		long long int top=0;
		limit *q=new limit [n+1];
		for(int i=1;i<=n;i++) //找到右端点
		{
			while(top!=0&&(q[top].element>p[i].element))
			{
				long long int lo=q[top].location;
				p[lo].high=i;
				top--;
			} 
			top++;
			q[top]=p[i];
		}
		while(top!=0)
		{
			long long int lo=q[top].location;
			p[lo].high=n+1;
			top--;
		}
		limit *qq=new limit [n+1];
		for(int i=n;i>=1;i--) //找到左端点
		{
			while(top!=0&&(qq[top].element>p[i].element))
			{
				long long int lo=qq[top].location;
				p[lo].low=i;
				top--;
			} 
			top++;
			qq[top]=p[i];
		}
		while(top!=0)
		{
			long long int lo=qq[top].location;
			p[lo].low=0;
			top--;
		}
		long long int maxn=0;
		for(int i=1;i<=n;i++)  //求出以每一个矩形为高的最大面积
		{
			long long int h=p[i].high;
			long long int l=p[i].low;
			long long int s=p[i].element*(h-l-1);
			if(s>maxn)
				maxn=s;
		}
		cout<<maxn<<endl;
		delete [] p;
		delete [] q; 
		delete [] qq; 
		cin>>n;
	}
 } 
发布了21 篇原创文章 · 获赞 4 · 访问量 898

猜你喜欢

转载自blog.csdn.net/zhL816/article/details/104972186