Largest Rectangle in a Histogram【单调栈】【双向】

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:
Largest Rectangle in a Histogram【单调栈】【双向】_第1张图片
Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
Input
The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1 <= n <= 100000. Then follow n integers h1, …, hn, where 0 <= hi <= 1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.
Output
For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.
Sample Input
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample Output
8
4000

题意

直方图包含多个高度不同,宽度相同的矩形,这些矩形构成整个直方图,求最终最大的矩形(可能由多个最初的矩形连接而成)面积

题解

用单调栈分别求出每个最初的矩形左边和右边高度大于等于它的矩形个数L[i]和R[i],然后计算以它为高,L[i]+R[i]+1为宽的矩形面积。从1到n遍历求出面积,取最大值即可

单调栈具体实现

以2,1,4,5,1,3,3为例,用一个单调递增的栈去存这些数的下标。
为了方便,我们在求L[i]时,以0先压入栈,对应a[0] = -1,以保证a[i]都会大于-1。第一步:0入栈。{0}
第二部:i=1 ;a[1] = 2>a[0] = -1 ; 1入栈。 计算入栈元素 L[1] = 当前元素下标-栈顶元素-1 = 1-0-1 = 0;表示2左边没有比它高的矩形。 {0,1}
第三部:i = 2; a[2] = 1<2 为了维护单调性,1出栈,继续判断 1>0 所以2入栈;计算L[2] = 2-0-1 = 1 表示1左边有一个比它高的矩形 {0,2}

第四部:4>1 5>4 单调递增性质不变 一直压栈即可 并计算L[3] = 0 ;L[4] = 0
{0,2,3,4}

第五步: 1 < 5 1 < 4 1=1 所以5,4,1一直出栈,然后再将 第五个元素1的下标5压入栈 计算L[5] = 5-0-1 表示左边有4个高于或等于它的矩形 {0,5}
第六步: 3>1 入栈,计算L[6] = 0 ; {0,5,6}
最后一步: 3=3 6出栈 7入栈 然后计算 L[6] = 1 {0,5,7}
以上就求出了 1~n的L[i] 。 R[i]只需将n+1压栈(a[n+1]=-1) 然后i从n遍历到1,R[i] = 栈顶元素-当前下标元素-1,入栈出栈条件同上即可。**

单调栈分析

上面应用单调栈求出了当前元素到左边第一个小于当前元素高度的矩形的距离。
那么为啥那么做是成立的?
大体上想象一下:
1.首先栈底有一个最小的元素-1 后面的元素不会小于它,由此保证了栈不会被清空。
2.如果后面来的元素比当前栈顶大(比较的是对应的值,下同),那么显然第一个小于当前元素高度的矩形就是栈顶元素,L[i] = 0,i入栈;
3.如果后面来的元素比当前栈顶小,而栈顶元素对应的值是当前栈内所有元素对应的值最大的! 所以可能存在其他元素比当前元素大,所以需把栈顶元素出栈,继续新的栈顶元素和当前元素的值,直到找到比栈顶元素小的元素(一定找得到,栈底元素最小保证了这点),然后再作下标差
4.有一些元素出栈了,是否会对求L[i]有影响? 考虑3.中的情景,栈顶元素大于当前元素,若该栈顶元素在当时入栈时,小于了之前的栈顶元素,导致之前的栈顶元素出栈,也是完全可能的。 但是之前的栈顶元素>=当前栈顶元素>=当前元素。而我们计算当前元素能够横跨多少个矩形时,是按照下标差计算的,所以即使之前的栈顶元素消失,也不会对判断造成错误。

代码

#include
#include
#include
#include
using namespace std;

int a[100500],l[100500],r[100500];
stack s;
int main()
{
	freopen("in.txt","r",stdin);
	int n; 
	while(~scanf("%d",&n) && n) {
		memset(a,0,sizeof(a)); memset(l,0,sizeof(l));memset(r,0,sizeof(r));
		while(s.size()) s.pop();
		for(int i  = 1;i<=n;i++) {
			scanf("%d",&a[i]);
		}
		a[n+1] = a[0]  = -1;  
		s.push(0); 
		for(int i = 1;i<=n;i++) {
			while(!s.empty() && a[s.top()] >= a[i]) s.pop() ;
			l[i] = i-s.top()-1;
			s.push(i); 
		}
		while(s.size() ) s.pop() ;
		s.push(n+1); 
		for(int i = n;i>=1;i--) {
			while(!s.empty() && a[s.top()] >= a[i]) s.pop() ;
			r[i] = s.top()-i-1;
			s.push(i); 
		}
		long long ans = -1;
		for(int i = 1;i<=n;i++) {
			long long area = 0;
			area = 1LL*(l[i]+r[i]+1)*a[i]; //防止溢出 
			ans = max(ans,area);
		}
		printf("%I64d\n",ans); 
		
	}
	
	return 0;
}

你可能感兴趣的:(Virtual,Judge题解,ACM)