hdu 1506 Largest Rectangle in a Histogram(单调栈)

来源:互联网 发布:php遍历对象数组 编辑:程序博客网 时间:2024/05/20 17:41

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1506

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

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 34 1000 1000 1000 10000
 

Sample Output
84000
 

Source
University of Ulm Local Contest 2003


第一次学习单调栈,参考大牛的http://blog.csdn.net/hopeztm/article/details/7868581

分析: 

如果采用枚举的方式,如果当前我们枚举项是 i = 0, 即 height = 2, 

我们用另外两个变量 j 和k 向左和向右两个方向搜素,找到第一个 小于 height的下标,这样我们就找到了用 i 项作为高度长方形了。

我们假设 -1位置,和最右高度都是无穷小。

例如:

i = 0, j = -1, k = 1, 最后的面积是 (k - j - 1) * height = 2

i = 1, j = -1, k = 7, 最后面积是( k - j - 1) * height = 7;

...

i = 3, j = 2, k = 5 面积是 ( k - j - 1) * height = 8 

枚举出所有的长方形的同时,然后得到最后的面积。

不过这样的程序的时间复杂度是 O(n^2)

我们如何能仅仅做一次,就求出这个面积呢?

观察:

当我们扫扫描到第一个高度 H1 = 2的时候,我可以标记它的起始位置1, 因为我们还不知道它将向右扩展到什么地方,所以继续扫面。

当遇到第二项 H2 = 1, 因为这项比之前的小,我们知道,用H1做高度的长方形结束了,算出它的面积。

同时这个时候,我们多了一个高度H2,用它做长方形高度的长方形起始位置应该是在哪里呢? 因为H1的高度比H2要高,所以这个起始位置自然是H1所在的位置。


为了模拟上面的过程,我们引入单调栈~

我们先定义我们我们要保存的每一项数据

struct Node

{

      int height;

      int startPosition;

};

用来描述某一个高度,和这个高度的起始位置。

然后我们按照高度来组织成单调栈。我们来看一下它是如何工作的。

为了不用考虑堆栈为空的情况,我们用插入栈底 一个高度(0, 0)的项。

数据: 

 2 1 4 5 1 3 3

这样初始化

(0 , 0)

I = 1

当扫描到(2, 1)时候,因为高度2 大于栈顶,插入

(0, 0),  (2, 1)

I = 2: 

当扫描到1的时候,因为1小于栈顶高度2, 我们认为栈顶的那个高度应不能再向右扩展了,所以我们将它弹出

这个时候扫描到 i = 2;

高度是 (i - 1(H1.startIndex)) * H1.height = 2;

我们得到一个面积是2的长方形。

同时我们发现高度是1的当前高度,可以扩展到 H1所在的下标,所以我们插入( 1, 1) 堆栈变成

(0, 0), (1, 1) 因为(2, 1)已经不能向右伸展了,已经被弹出了


i = 3

(0, 0), (1, 1), ( 4 3)

i = 4

(0, 0), (1, 1), (4, 3), (5, 4)

i = 5 

这个时候当前高度小于栈顶高度,我们认为栈顶已经不能向右扩展,所以弹出,并且获得面积 ( i  - H5.startindex) * H5.height = (5 - 4 ) * 5 = 5

弹出这个元素后,其实(4, 3)的高度也要比 1 大,所以把这个也弹出来,同样方式获得面积 8.

最后我们的堆栈是

(0, 0) , (1, 1)

i  = 6

(0, 0), (1, 1), ( 3, 6)

i = 7

(0, 0), (1, 1), (3, 6)

i = 8

最后一步是有点特殊的,因为我们必须要把所有的元素都弹出来,因为栈里面的高度,都坚持到了最后,我们要把这些高度组成的长方形拿出来检测。

我们可以假设扫面到8的时候,高度是0,(最小值)

弹出(3,6)获得面积 (8 - 6 ) * 3 = 6

弹出(1, 1)获得面积(8 - 1) * 1 = 7


最后的面积是8.



STL的栈

代码:

#include<stdio.h>#include<string.h>#include<string>#include<math.h>#include<iostream>#include<algorithm>#include<stack>using namespace std;const int maxn=1000005;__int64 H[maxn];int n;struct Node{    __int64 height;    int pos;    Node(__int64 h,int p):height(h),pos(p)  //构造函数    {    }};__int64 get_area(int n){    stack<Node>st;    __int64 height,max=0;    int cur;    __int64 cur_area;        //当前得到的矩阵面积    st.push(Node(-1,0));      //初始化栈    for(int i=0;i<=n;i++)    {        cur=i+1;        if(i==n)              //如果到结尾,设为0            height=0;        else            height=H[cur-1];        Node t(height,cur);        while(st.top().height>height)   //当前点与栈顶元素比较        {            t=st.top();                  //如果小于栈顶元素,则弹出栈顶元素,并计算当前得到的面积            st.pop();            cur_area=(cur-t.pos)*t.height;            if(cur_area>max)                max=cur_area;        }        st.push(Node(height,t.pos));      //如果当前点大于栈顶元素,则插入到比他小的位置    }    return max;}int main(){    //freopen("in.txt","r",stdin);    while(scanf("%d",&n)&&n)    {        for(int i=0;i<n;i++)            scanf("%I64d",&H[i]);        printf("%I64d\n",get_area(n));    }    return 0;}

手动模拟的栈

代码:

#include<stdio.h>#include<string.h>#define max(x,y) x>y?x:yconst int maxn=1000005;__int64 s[maxn]={-1};    //初始化栈不为空int p[maxn];            //存储前面有比它小的个数int main(){    //freopen("in.txt","r",stdin);    int n;    while(scanf("%d",&n)==1)    {        if(n==0) break;        memset(p,0,sizeof(p));        int top=0;        __int64 ans=0;        __int64 h;        for(int i=1;i<=n+1;i++)        {            if(i!=n+1)                scanf("%I64d",&h);            else                h=0;            if(h>s[top])        //与栈顶元素比较,比它大,则入栈            {                s[++top]=h;                p[top]=1;       //初始化            }            else            {                int cnt=0;                while(s[top]>=h)      //如果比栈顶元素小,则弹出栈顶元素,计算当前面积                {                    ans=max(ans,(cnt+p[top])*s[top]);                    cnt+=p[top--];                }                s[++top]=h;          //将它插入到比他小的位置                p[top]=cnt+1;            }        }        printf("%I64d\n",ans);    }    return 0;}



0 0
原创粉丝点击