• 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

  • 相关阅读:
    javascript中的继承实现
    【414】Code::Blocks增加主题
    【413】C 语言 Command line
    【412】Linux 系统编译 C 程序
    【411】COMP9024 Assignment1 问题汇总
    【410】Linux 系统 makefile 文件
    Solr的入门知识
    Java8新特性
    Linux命令大全
    为博客园博文添加目录的两种方法
  • 原文地址:https://www.cnblogs.com/YangKun-/p/12511013.html
Copyright © 2020-2023  润新知