POJ-2559 Largest Rectangle in a Histogram【单调栈】

    单调栈  

    单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置,利用O(n)的复杂度来处理一些看似复杂的问题

     假如我们想找每个数右面的第一个比他严格大的数,暴力?当然可以,那有没有什么更好的方法,利用贪心的思想,从后往前计算,如果a[i]对应的f[i]已经算出,而且a[i]<=a[j],那么a[i]对f[1-j]一定没有影响,因为在1-(j-1)时与其取i的下标还不如取j的下标,所以在枚举的过程中找到一个比当前大的数就跳出,后面的数我们不管

     这样对整个序列贪心,维护一个只保存有用元素的下标的栈,设栈顶为top,当前元素的下标为j 弹出栈顶的下标直到栈顶元素比当前元素大了(维护单调减的栈)就说明不能继续贪心了,记录更新答案,然后将当前下标入栈……(最靠前的可行的元素就是栈顶)

 

可以用来求寻找他的左面/右面第一个小于/大于他的数或者他的位置 POJ-3250

给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大     POJ-2559     POJ 3494

给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大 POJ-2796  

HDU-5033

    while (top > 0 && s[top - 1] <= num) top--;//把破坏栈单调性的元素弹出
    s[top++] = num;//下一个元素入栈
//此时栈中存的是元素的值

POJ-2559 Largest Rectangle in a Histogram

题意:在一条水平线上有n个宽为1的矩形,求包含于这些矩形的最大子矩形面积

 

 

  

要找最大的矩形的面积暴力一点的话,枚举每一个单位长度为1的矩形,分别向左右延伸,Smax=weight*hight复杂度O(n^2),我们的目的只是找到每个矩形向左右延伸的广度,就可以利用单调栈来做,这样的时间复杂度只有O(n)。本题压入栈中的是元素的值,但貌似压入编号更为常用,不过没关系,道理是一样的。

以题中样例为例:height =[2,1,5,6,2,3],

1) 刚开始栈中为空,压入2;然后当i=1时,2<=1不成立,下标出栈,栈变为空,计算此时面积res=2;

 

 2)高度1,5,6是依次递增的,依次压入栈中;当i=4时,6>2,6出栈,所以计算此时的面积res=6,如图:

3)栈顶元素5, 5>2(此时,还要和下标i=4的高度比较),所以,编号为2的单位5出栈,计算面积为res=10;

 

 4)因为栈顶元素为1,其对应的高度为1<2,满足条件,所以,将a[i]入栈,直到i=6(为压入的0);此时栈中的元素为{1,2,3},有一个栈顶元素对应的高度大于i=6对应的高度,所以,5出栈,计算面积为3,其中最大面积为10;

 

5)栈顶元素为2, 2>0,所以,2出栈,计算面积为4,最大面积为10;

6) 此时,栈顶元素为1, 1>0,所以,1出栈,因为1出栈以后,栈变为空,所以,计算面积的方式有变化,res=height[1]*6=6,最大面积为10。

 

   可以维护一个单调递增的栈,用一个数组记录该高度能延伸的最长的距离,将之前比这高的矩形弹出栈直到遇到比新矩形低的矩形,同时累计他们的宽度之和,乘以这个更矮矩形的高度来更新答案。最后再按这个方法把所有矩形弹出去来更新答案,所以我们要在最结尾压入一个高度为0的单位,以保证栈中的所有元素都被弹出。

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 #define ll long long
 5 const int inf=0x3f3f3f3f;
 6 const int maxn=100009;
 7 int a[maxn],z[maxn],num[maxn];
 8 int main()
 9 {
10     int n;
11     while(scanf("%d",&n),n)
12     {
13         for(int i=1;i<=n;i++)
14         {
15             scanf("%d",&a[i]);
16         }
17         a[n+1]=0;// 保证栈中的所有单位都被弹出
18         int top=0;//模拟栈
19         ll maxx=0;
20         for(int i=1;i<=n+1;i++)
21         {
22             ll tem=0;
23             while(top>0&&z[top]>=a[i])//将大于当前的高度弹出
24             {
25                 tem+=num[top];//累计宽度
26                 maxx=max(maxx,z[top]*tem);//更新Smax
27                 top--;//弹出
28             }
29             z[++top]=a[i];//入栈
30             num[top]=tem+1;//要把弹出的单位的总宽度赋给下一个入栈的元素
31 //相当于把前面弹出的单位合并给下一个入栈的单位了??
32         }
33        printf("%lld
",maxx);
34     }
35     return 0;
36 }

 

图片来自:https://www.cnblogs.com/love-yh/p/7182920.html

原文地址:https://www.cnblogs.com/YangKun-/p/12511013.html