单调队列——最大矩形面积

目录

  • 题目描述
  • 分析
  • 思路
  • 易错点
  • 代码

题目描述:最大矩形面积

直方图是由在公共基线处对齐的一系列矩形组成的多边形。矩形具有相等的宽度,但可以具有不同的高度。例如,左边的图显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,以单位为单位测量,其中1是矩形的宽度: 

\"\"


通常,直方图用于表示离散分布,例如,文本中的字符频率。注意,矩形的顺序,即它们的高度,是重要的。计算在公共基线对齐的直方图中最大矩形的面积。右图显示了所描绘直方图的最大对齐矩形。

输入

输入包含几个测试用例。每个测试用例描述一个直方图,并以整数n开始 ,表示由它组成的矩形数。您可以假设 1 <= n <= 100000。然后跟随 n个整数 h 1,...,h n,其中 0 <= h i <= 1000000000。这些数字以从左到右的顺序表示直方图的矩形的高度。每个矩形的宽度为 1。在最后一个测试用例的输入之后为零。

产量

对于每一个测试用例,在单行上输出指定直方图中最大矩形的区域。请记住,此矩形必须在公共基线对齐。

样本输入

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

样本输出

8
4000

暗示

建议使用大量输入scanf。


 分析

看到这题,肯定想到的是枚举一遍高度,再找左边比他小和右边比他小的两个矩形,再求面积。

这道题数据范围特别大,不可能用一重循环以上的代码过,所以这种做法是错误的,这一道题怎样才能用一重循环完成就成了难点。那么这时就该想到单调栈。


思路

单调队列在滑动窗口中提及过,就是有单调性的栈,那单调栈就是很单调,枯燥的栈有单调性的栈,即从栈底到栈顶的数都是递增或递减的。

这道题又该怎么做呢?首先设该矩形左端点为left,右端点为right,则矩形面积为 ( right -  left ) * a[ i ] . h 

将其依次放入栈中,按从小到大,维护其单调性,使其高度为递增的

\"图片\"

如果第 i 个矩形的高大于栈顶元素的高,就将其放入栈中

如果第 i 个矩形的高小于栈顶元素的高,就说明 栈顶这个矩形的右端点是 i ,而 i 左端点就为栈顶矩形的左端点

\"图片\"

思路大致就是这样(如果没有懂,可以看代码,结合理解)


易错点

如果只循环 n 次,有可能栈不是空的,还有矩形没有计算


代码

代码大致就这样啦

结合思路理解

#include <cstdio>
#include <iostream>
#include <stack>
#define ll long long
using namespace std;
 
ll n;
 
struct number{
    ll h ;
    ll le ;
};
 
int main()
{
    while(1)
    {
        stack <number> Q ;//定义一个栈 
        number a[100005];
        ll ans = 0 ;//记录答案 
        scanf(\"%lld\", &n );
        if(n == 0) return 0;
        for(int i = 1 ;i <= n ; ++ i ) {
            scanf(\"%lld\", &a[i].h );
            a[i].le = i ;//让每一个矩形的左端点都初始化为自己 
        }
        Q.push(a[1]);
        a[n+1].h = 0;//用来在最后将栈清空 ,血的教训!!! 
        for(int i = 1 ; i <= n + 1 ; ++ i ) {
            if( a[i].h > Q.top().h )//如果当前高度大于栈顶高度 
                Q.push(a[i]);//入栈 
            else
            {
                ll left = 0;//记录栈顶元素的左端点 
                while(!Q.empty() && a[i].h <= Q.top().h )
                {
                    number j ;
                    j.h = Q.top().h ;
                    j.le = Q.top().le ;
                    Q.pop() ;
                    ans = max( ans , ( i - j.le ) * j.h );//计算当前大矩形的面积 
                    left = j.le ;//记录其左端点 
                }
                a[i].le = left ;//更新第 i 矩形的左端点 
                Q.push(a[i]);
            }
        }
        printf(\"%lld\\n\", ans );
    }
    return 0;
}

 

收藏 打印