C++解题报告:最大矩形面积(Largest Rectangle in Histogram)———单调栈

目录

目录

题目描述

初步探究

定义

正文

代码

总结

 


前言

此篇博客是我在CSDN发表的第一篇博客,现在看来文笔比现在差了许多。果然写博客确实提高了我的能力。单调栈是一个非常重要的数据结构,在我们的DP学到后期时,许多题目都需要用他来优化,他常常可以将O(N^{3})的算法优化为O(N^{2})(甚至更少)。但这里我要讲的不是单调队列优化DP,而是对另一种算法的优化。(如果你想看看单调队列优化DP,你可以看看我的更多博客)


题目描述

在X轴上水平放置着 N 个条形图,这 N 个条形图就组成了一个柱状图,每个条形图都是一个矩形,每个

矩形都有相同的宽度,均为1单位长度,但是它们的高度并不相同。

例如下图,图1包含的矩形的高分别为2,1,4,5,1,3,3 单位长度,矩形的宽为1单位长度。

你的任务就是计算柱状图中以X轴为底边的最大矩形的面积。图2阴影部分就是上述例子的最大矩形面积。

输入

输入数据的第一行是一个整数 N(1≤ N ≤100000),表示柱状图包含 N 个矩形。

紧接着 N 个整数h1,...,hn(0≤ hi ≤20000, 1≤ i≤ N),表示柱状图中按从左到右顺序给出的矩形

的高度。矩形的宽度为1。

 

输出

 

输出一个整数S,表示以X轴为底边的最大矩形的面积。

 

样例输入

7
2 1 4 5 1 3 3

样例输出

8

初步探究

拿到这道题,如果一个人没有学过单调栈,那么第一思路就是爆搜或者是DP;爆搜:题目N的数据范围(1≤ N ≤100000),而爆搜的数据范围是O(N^2),显然不可取,而动规,则不满足一个状态能由另一个状态转移过来这一基本原则。既然两种方法都不行,那么就要请我们今天的主角:单调栈登场了。


定义

单调栈是一种类似于单调队列的数据结构。

单调栈,顾名思义,也就是很单调的栈,是一个具有单调性的栈。最简单的单调性就是保持递增或递减,比如 1 2 3 4 就是一个单调的序列,而2 3 4 1则不是。如果要维护一个上升的单调栈,入栈时,如果栈为空或栈顶小于当前元素,则入栈;如果栈顶大于当前元素,则弹出栈顶,直到栈顶小于当前元素或栈为空为止。


正文

说了这么多,现在就该切入正题了。我们不妨假设当前存在一个最大矩形,它的右端点为i,而左端点为pos,它的高度为h[i],并且在这中间每个小矩形的h[j]都会大于于h[i](没错,这已经接近单调栈了),也就是一个长为(i-pos),宽为h[i]的长方形,,那么它的面积为(i-pos)*h[i],也就是这样的:

C++解题报告:最大矩形面积(Largest Rectangle in Histogram)———单调栈_第1张图片

(图画的不好,讲将就着看吧)

所以我们就可以建立一个单调栈,保持它的递增性,并把栈中的每个元素都设为结构体,h表示它的高度,pos表示为离他最远的比h小的矩形坐标(初始化为i),然后维护它的单调性。当如图上的情况(栈顶元素大于当且当前元素的高度时),便可以统计它的最大面积,并且用一个maxx记录最大答案。当整个循环结束时,就将当前i的pos设为最后一个被弹出去元素的pos。因为最后的栈可能非空,所以可以再循坏一次保证找到最优解。

(没看懂没关系,结合题解的代码很好理解)

代码

#include 
#include 
#include 

using namespace std;

#define inf 0x7f7f7f7f
#define N 101001
#define LL long long

int read() {
    int f=1,s=0;char a=getchar();
    while(!(a>='0'&&a<='9')) {if(a=='-') f=-1 ; a=getchar(); }
    while(a>='0'&&a<='9') {s=s*10+a-'0'; a=getchar();}
    return f*s;
}

struct node {
    LL h , pos;//pos为当前矩形的最左端点
}a[N];

LL n,maxx=0,pos;
stack q;

int main() {
    n=read();
    for(int i=1;i<=n;i++)
        a[i].h=read(),a[i].pos=i;//每个小矩形的左端点初始化为自己,并记录每个矩形的高度
    for(int i=1;i<=n+1; i++) {
        if(q.empty() || q.top().ha[i].h) {
                node x=q.top();//x记录栈顶元素
                LL sum=0;//sum统计当前最大矩形面积
                q.pop();
                sum=x.h*(i-x.pos);//右端点为i,左端点为pos
                maxx=max(maxx,sum);//记录答案
                pos1=x.pos;//继承左端点
            }
            node G;//把新元素放入栈
            G.h=a[i].h;
            G.pos=pos1;
            q.push(G);
        }
    }
    cout<

总结

此题是单调栈一个很典型的应用问题,十分适合新人上手。

感兴趣的可以看看这道题,也是单调栈的应用

洛谷OJ P1886滑动窗口

单调栈(单调队列)是一个十分有用的数据结构,多重背包的优化也可以应用到它(感兴趣的同学可以百度一下)

笔者第一次写博客,文笔不好请见谅

 

你可能感兴趣的:(C++,单调队列,单调队列专栏——从入门到放弃)