POJ2559 Largest Rectangle in a Histogram(单调栈)
Largest Rectangle in a Histogram
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 24316 | Accepted: 7866 |
Description
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:
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.
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
Hint
Huge input, scanf is recommended.
Source
题解:
单调栈计算每个矩形往左、往右所能够达到的最远距离。(入栈判断左边界,出栈判断右边界)
PS:我用的是左开右闭区间。
一个小技巧:栈中直接维护位置信息,这样就不用开两个栈或是结构体了。
2.解题思路
如果确定了长方形的左端点L和右端点R,那么最大可能的高度就是min{h[i]|L<=i<R},就可以得到一个时间复杂度为O(n^3)的算法。如果对计算区间最小值进行优化,就可以得到一个O(n^2)的算法,但是仍然超时。接下来想一下,如何高效的求解。
设面积最大的长方形的左端点是L,右端点是R,高度是H。如果h[L-1]>=H,那么左端点就可以更新为L-1,从而可以得到更大的长方形,与假设矛盾,所以h[L-1]<H,同理h[R]<H.H=min{h[i]|L<=i<R}。我们固定可以给出这样的H的i并进行分析。L是满足h[j-1]<h[i]的最大的j(j<=i),R是满足h[j]<h[i]的最小的j(j>i)。如果能够求出L[i],R[i],那么最大面积就是max{h[i]*(R[i]-L[i])|0<=i<n}.
L[i]和R[i]可以利用单调栈快速求解。
我们先考虑求L的情况。
初始化一个栈,不断增加i的值。对于每一个i,若栈顶元素大于等于h[i],就退栈,若栈为空,则L为0,否则L就是栈顶元素+1,然后把i加入栈中,维护单调栈。可以知道栈是单调的,而且栈顶元素最大。
求R的方法类似。
时间复杂度为O(n)。
1 #include<iostream> 2 #include<stack> 3 #include<algorithm> 4 #include<string> 5 #include<cstring> 6 #include<cstdio> 7 using namespace std; 8 int h[100005]; 9 int l[100005]; 10 int r[100005]; 11 stack<int>s; 12 long long max1(long long a,long long b) 13 { 14 15 if(a>=b) return a; 16 else return b; 17 } 18 int main() 19 { 20 int n; 21 while(~scanf("%d",&n)) 22 { 23 if(n==0) break; 24 for(int i=1;i<=n;i++) 25 scanf("%lld",&h[i]); 26 27 while(!s.empty()) s.pop(); 28 for(int i=1;i<=n;i++) 29 { 30 while(!s.empty()&&h[s.top()]>=h[i]) s.pop();//向左找找到比它矮的就跳出 31 if(s.empty()) l[i]=1;//如果此时之前没东西,那就是从1开始 32 else l[i]=s.top()+1;//否则就是它前面的一个 33 s.push(i); 34 } 35 36 while(!s.empty()) s.pop(); 37 for(int i=n;i>=1;i--) 38 { 39 while(!s.empty()&&h[s.top()]>=h[i]) s.pop();//向右找找到比它矮的就跳出 40 if(s.empty()) r[i]=n;//如果此时之前没东西,那就是可以一直到最右端 41 else r[i]=s.top()-1;//否则就是它前面的一个 42 s.push(i); 43 } 44 45 long long s=0; 46 for(int i=1;i<=n;i++) 47 { 48 s=max1(s,(long long)h[i]*(r[i]-l[i]+1));//以i为中心,找到了左右就可以求面积了 49 } 50 51 printf("%lld\n",s); 52 } 53 return 0; 54 }
用数组模拟
1 #include<iostream> 2 #include<stack> 3 #include<algorithm> 4 #include<string> 5 #include<cstring> 6 #include<cstdio> 7 using namespace std; 8 int h[100005]; 9 int l[100005]; 10 int r[100005]; 11 int st[100005]; 12 long long max1(long long a,long long b) 13 { 14 15 if(a>=b) return a; 16 else return b; 17 } 18 int main() 19 { 20 int n; 21 while(~scanf("%d",&n)) 22 { 23 if(n==0) break; 24 memset(st,0,sizeof(st)); 25 for(int i=0;i<n;i++) scanf("%d",&h[i]); 26 27 int t=0; 28 for(int i=0;i<n;i++) 29 { 30 while(t>0&&h[st[t-1]]>=h[i]) t--; 31 l[i]=(t==0?0:(st[t-1]+1)); 32 st[t++]=i; 33 } 34 35 t=0; 36 for(int i=n-1;i>=0;i--) 37 { 38 39 while(t>0&&h[st[t-1]]>=h[i]) t--; 40 r[i]=(t==0?n:st[t-1]); 41 st[t++]=i; 42 } 43 long long s=0; 44 for(int i=0; i<n; i++) 45 { 46 s=max1(s,(long long)h[i]*(r[i]-l[i]));//以i为中心,找到了左右就可以求面积了 47 } 48 49 printf("%lld\n",s); 50 } 51 return 0; 52 }