Largest Rectangle in a Histogram POJ - 2559 单调栈详解

http://poj.org/problem?id=2559

Largest Rectangle in a Histogram POJ - 2559

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.
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.

题意:在这个图形中找到一个面积最大的矩阵,这道题是单调栈的入门题。

暴力思路:这道题我记得暴力好像是能过得(数据水),就是枚举每一个柱形往两边延伸,找到最大的,复杂度非常大。

单调栈思路:这道题主要是怎么找到这n个柱子,能往两边延伸的最大长度。

所以用单调栈维护从左到右的柱子高度的单调性。栈内元素从小到大。若要加入一个元素,那么要把栈内大于等于这个元素的所有值出栈,然后把这个元素入栈。(维护的是单调栈的单调性)

然后思考这个问题,既然这个栈是单调的,那么栈里的每个元素都是大于他左边的元素的。那么也就是说这个元素不能再向左延伸了(因为要他自己的高度向两边延伸到最远,左边元素小于他,所以不能延伸) 相反这个元素能往右延伸到哪里呢,延伸到这个栈的结尾的地方。(因为要他自己的高度向两边延伸到最远,他右边元素都大于他,所以都能延伸)

什么时候一个元素结束延伸呢。当这个元素出栈的时候,出栈的条件是有一个比他小的高度要加入栈,这个时候这个高度肯定是要出栈的,那么怎么统计他向左右延伸的距离呢。

在我们存储这个栈的信息有,这个元素的高度,和这个元素能往左延伸多远(包括自己)。
元素高度      :1  3  5  6  7  

向左延伸距离:1  2  1  2  1
现在有一个高度4到加入栈,那么栈顶元素7>4不能再向右延伸,所以7出栈,长度为1,高度为7.   1*7=7

此时将7出栈,用一个tmp存储当前出栈元素延伸距离和,因为接下来要出站的元素 可以延伸到7能延伸到的所有位置。(因为7左边的元素小于7)   此时tmp=1;
元素高度      :1  3  5  6

向左延伸距离:1  2  1  2

现在这个高度4到想要加入栈,那么栈顶元素6>4,不能再向右延伸,所以6出栈,长度为tmp+2,高度为6.   3*6=18
此时tmp=3;
元素高度      :1  3  5 

向左延伸距离:1  2  1 
现在这个高度4到想要加入栈,那么栈顶元素5>4,不能再向右延伸,所以5出栈,长度为tmp+1,高度为5.   4*5=20

此时tmp=4;

元素高度      :1  3 

向左延伸距离:1  2 
此时3<4,将4加入栈中
元素高度      :1  3       4

向左延伸距离:1  2   tmp+1

这些操作就是单调栈如何维护单调性,和如何计算答案。但有些人会说,道理倒是懂,但是这样的思路好难想,怎么办?
其实现在不用担心,熟能生巧嘛。做的多了就能想到了。


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <math.h>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof a)
#define LL long long
const LL inf=0x3f3f3f3f;
const LL mod=1e9+7;
const int N=101000;
const int M=405;
using namespace std;
int a[N];
int q[N],num[N];//q数组是栈,num数组表示往左延伸的距离
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        a[++n]=0;//最后应该将栈所有元素都出栈计算答案,那么直接在最后添个0就行了
        int l=1,r=0;//栈头,和栈未
        LL ans=0;
        for(int i=1;i<=n;i++)
        {
            int tmp=0;
            while(l<=r&&q[r]>=a[i])//将所有大于等于a[i]的元素出栈(等于a[i]的元素,留谁在栈里都一样,所以直接出栈也没关系)
            {
                tmp+=num[r];
                ans=max(ans,q[r]*(LL)tmp);//出栈时更新答案
                r--;
            }
            q[++r]=a[i];//入栈操作
            num[r]=tmp+1;
        }
        printf("%I64d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/xiangaccepted/article/details/80019376
今日推荐