单调栈

单调栈

所谓单调栈即一个具有单调性(呈递增或者递减状态)的一个栈(先进后出),一般用于一个求最近的最小或最大值中,能发挥奇效。
注意能使用单调栈题目一般都可以用暴力解决,且具有一定单调性质。

下面来一个简单单调栈题目:

                            直方图中最大的矩形

直方图是由在公共基线处对齐的一系列矩形组成的多边形。矩形具有相等的宽度,但可以具有不同的高度。
例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1:通常,直方图用于表示离散分布,例如,文本中字符的频率。现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。图例右图显示了所描绘直方图的最大对齐矩形。

在这里插入图片描述

输入格式输入包含几个测试用例。每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目。然后跟随n个整数h1,…,hn。这些数字以从左到右的顺序表示直方图的各个矩形的高度。每个矩形的宽度为1。同行数字用空格隔开。当输入用例为n=0时,结束输入,且该用例不用考虑。

输出格式对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。每个数据占一行。请注意,此矩形必须在公共基线处对齐。
数据范围1≤n≤100000,0≤hi≤1000000000

我们先来理解一下题目

  • 首先里面所有的长条都是同一个底,只有高可能不一样;
  • 所以我们可以根据他的高(即长度)去探索,我们发现每一条长条所能延伸成的最大矩形面积必定为他的 左边第一个小于他高度 ( left( 满足此条件的长条位置 ) ) 和他 右边第一个小于他高度 ( right ) 的矩形的距离乘上他的高度(h),即 Smax_h[ i ] = ( right -left )*h[ i ];
  • 如果直接用暴力会很麻烦,所以我们根据题目不难发现,我要找到左边最近的第一个小于此长条的长条位置,就是根据单调栈的性质来的。
  • 具体怎么操作下面看代码:
#include<stdio.h>
int h[100010];
int right[100010];
int p[100010];
main()
{
    int n,i,j,k,r;
    long long max,left;//注意left为long long下面会给出解释。
    while(~scanf("%d",&n)&&n!=0)
    {
        for(i=0;i<n;i++)
        {
            scanf("%d",&h[i]);
        }
        max=h[0];
        r=-1;
        for(j=n-1;j>=0;j--)
        {
            while(r>=0&&h[p[r]]>=h[j])r--;/*当栈里面的数据大于等于
                          此长条的高,则出栈。当栈为空或者小于则跳出*/
            if(r<0)right[j]=n;/*当栈为空,说明此长条往右没有满足条件
                          的长条。则只能为最右边的长条。*/
            else right[j]=p[r];//将满足条件的长条位置放入数组中。
            r++;
            p[r]=j;//入栈
        }
        k=-1;
        for(i=0;i<n;i++)
        {
            while(k>=0&&h[p[k]]>=h[i])k--;//同上执行左边操作。
            if(k<0)left=-1;/*因为右边的数据已经存了,所以左边的数据只
                         需用一次,所以不用开辟数组。*/
            else left=p[k];//同上
            k++;
            p[k]=i;
            if((right[i]-left-1)*h[i]>max)max=(right[i]-left-1)*h[i];
           //应为max可能会超出int范围,所以将left改为longlong 防止溢出
        }
        printf("%lld\n",max);
    }
}

注意为什么当栈为空时,right和left要赋值为n和-1。因为我们每次取的长度是满足条件的长条的上一个(right)或者下一个(left)因为我们不能取比此长条短的长条,但我们写的终止条件是在第一个比自己高的小的时候停止,所以他的上一个或者下一个必定不会短于此长条高度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值