程序设计思维与实践 Week5 作业 A 最大矩形

题目描述:

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

Input:

输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, ..., hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。

Output:

对于每组测试数据输出一行一个整数表示答案。

思路:

对于每个矩形,如果当前矩形开始分别向左和向右查找其可以构成大矩形的边界,时间复杂度为O(n^2)

下面是O(n)的做法:维护一个单调递减栈,即从栈顶到栈底元素值递减,栈中存放的是矩形的下标。然后从第一个矩形开始向右侧扫描,对于扫描到的当前的矩形i,其高度为a[i],如果a[i]>a[s.top()],则s.push(i),即:满足递减栈的性质,将矩形下标入栈。如果a[i]<a[s.top()],此时如果再将i入栈,则违反了递减栈的规则。此时弹出栈顶,记为j,矩形高度为a[j],现在考虑矩形j能够形成的最大面积。显然已经有a[i]<a[j],即:i位置为j能构成的区域的右边界。那么左边界呢?因为j为栈顶元素,且已经从栈顶弹出,所以此时的栈有两种可能:1.栈空(j是栈底元素),在这种可能下,左边界为1,因为栈空了,说明j要么就是1号矩形,要么j左边的矩形都会比矩形j高一些,只有这样j才是栈底元素,弹出后导致栈空。2.栈不空,那么设新的栈顶为k,显然有a[k]<a[j],此时的左边界为k,也就是说k~j之间的矩形都比j要高一些,只有这种情况才会构成kj在栈中挨着。找到了l和r之后,高度为a[j],也就找到了所构成的面积,更新ans。重复上述步骤,直到可以将a[i]放入栈中。

在此做法下,最终可能会形成一个递减栈。即:从栈顶到栈底,对应的矩形不断变矮。此时的r为栈顶元素(后面都是用到的同一个r值,也就是一开始的栈顶)。不断地出栈,对出栈的元素i求解构成的面积,高为a[i],右边界已经确定。那么左边界?若取出i后栈空,则l=1.否则l=s.top().(取出i后新的top),原理与上面分析的类似。

#include<iostream>
#include<stack>
using namespace std;
stack<long long> s;
long long n,ans,a[100010];
long long now,l,r;
int main()
{
	while(1)
	{
		cin>>n;ans=0;
		if(n==0) break;
		for(int i=1;i<=n;i++)
		cin>>a[i];
		for(int i=1;i<=n;i++)//枚举每个矩形 
		{
			if(s.empty()||a[i]>=a[s.top()])
			s.push(i);
			else
			{
				while(!s.empty()&&a[i]<a[s.top()])
				{
					now=s.top(),r=i-1,l;
					s.pop(); 
					if(s.empty())
						l=1;
					else
						l=s.top()+1;
					ans=max(a[now]*(r-l+1),ans);
				}
				s.push(i);
			}
		}
		if(!s.empty())
		r=s.top();
		while(!s.empty())//最终可能会形成一个递减栈 
		{
			now=s.top();
			s.pop();
			if(s.empty())
				l=1;
			else
				l=s.top()+1; 
			ans=max(a[now]*(r-l+1),ans);
		}
		cout<<ans<<endl;
	}
	return 0;
}
发布了277 篇原创文章 · 获赞 222 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/cax1165/article/details/104955734