深入解析:洛谷SP1805 Largest Rectangle in a Histogram 单调栈

一.题目

题目传送门

题目描述:在一堆的柱状图中找到一个最大的矩形,并输出矩形的面积

如图所示:

二.题目分析

答案矩形的高度

我们先去寻找一下这些矩形的规律:

  1. 答案矩形的下底面肯定是贴着地的,因为这些柱子的底面在同一平面,答案矩形肯定不是飘着的

  1. 答案矩形的上底面肯定是某一个柱子的顶部,比如上图的矩形,它的上底面就是第三个矩形的上底面,一但没有贴着某一个柱子的上底面,那一定还可以向上拓展。

以上两个规律应该显而易见可以得到,大家可以自己想几个样例试一试。

那么我们只需要去算出,以每一个柱子的高度答案矩形高度的矩形面积,并使用ans数组不断更新就可以了

那么矩形的高度已经解决了,接下来就是矩形的宽度了。

答案矩形的宽度

如图:

我们对第四个高度为2的柱子进行考察:

最左边:可以画到第二个柱子

最右边:可以画到第六个柱子

宽度为:6-2+1=5

我们也可以理解为:向左向右分别寻找第一个比考察柱子短的柱子,把这两个的标号相减再-1就是宽度

最左边:第一个柱子是第一个小于考察柱子的柱子,为1号柱

最右边:第七个柱子是第一个小于考察柱子的柱子,为7号柱

宽度为:7-1-1=5

那么单调栈的作用,就是便于我们去寻找两边的第一个小于目标柱子的柱子。

三.什么是单调栈

顾名思义,单调栈就是栈里的所有的元素都是单调递增单调递减,那么在这个题目中,我们需要使用单调递增的栈

四.代码和解释

struct n {
    int height, index;
}temp;
stack<n> s;

首先建立一个结构体,存储的是每一个元素的高度(height)和下标(index),并建立一个存储结构体n的栈。

if (s.empty() || s.top().height < a) {
    temp.height = a;
    temp.index = i;
    s.push(temp);  
}

如果栈为空,或者新的元素是大于栈顶元素的,那么新来的元素不会破坏栈的单调性,那么就把这个柱子入栈。

特别注意:这里的s.empty()s.top().height < a不能调换顺序,包括后面的判断也要先判断栈是否为空。c++中的&&和||是短路与短路或,但一个栈是空的时候,我们再去调用这个栈的栈顶,会报错,所以我们先去判断栈时候为空,如果是空的话,表达式s.empty() || s.top().height < a 已经为true了,那么这个表达式就被||短路了,也就是s.top().height < a就不会被执行了,也就是top一定不会去作用到一个空栈上。包括下面的!s.empty() && s.top().height >= a的表达式不能调换位置

else if (!s.empty() && s.top().height >= a) {
    while (!s.empty() && s.top().height >= a) {
        temp_height = s.top().height;
        s.pop();
        if (s.empty()) {
            length = i - 1;
        } else {
            length = i - s.top().index - 1;
        }
        ans = max(ans, (long long)length * temp_height);
    }
    temp.height = a;
    temp.index = i;
    s.push(temp);
}

这里的话我们去判断,如果栈不为空的时候并且新来的元素小于栈顶元素,那么新来的元素就破坏了栈的单调递增

那么我们就需要把这个栈的栈顶出栈,直到这个数进栈后不会破坏这个栈的单调递增。

我们用temp_height来存储这个栈顶的元素的高度,此时搞定了矩阵的高度。

至于矩阵的宽度,我们想一下,此时加进去的a比栈顶元素矮,那么a这个元素一定是栈顶元素右侧第一个小于a的元素,此时我们把栈顶元素出栈,此时的栈顶元素就是左侧第一个小于的元素

也就是说,对于一个单调递增的单调栈,考察第i个元素,那么可知,第i-1个元素是i左侧第一个小于i的元素,这个规律大家可以自己想几个测试数据,多画一画,应该不难得出。

当然如果当前柱子已经是当前栈的唯一元素,我们就加一个特判。

最后更新ans,并将新的数据进栈。

if (!s.empty() && i == num) {
    while (!s.empty()) {
        temp_height = s.top().height;
        temp_w = s.top().index;
        if (s.size() == 1) {
            length = temp_w;
            s.pop();
        } else {
            s.pop();
            length = temp_w - s.top().index;
        }
        ans = max(ans, (long long)length * temp_height);
    }
}

最后,我们要处理一下,i=num时,将栈中的剩余元素处理一下。

逻辑和刚才一样,同时对栈中只有唯一元素时特判一下。

然后由于题目要求有多组测试,所以把数组和变量清空一下

num = a = length = temp_height = temp_w = 0;
ans = 0;
while (!s.empty()) s.pop();

此时,游戏结束,如有不懂,可以私信我

AC代码:

#include<iostream>
#include<stack>
using namespace std;
struct n {
    int height, index;
}temp;
stack<n> s;
int num, a, length, temp_height, temp_w;
long long ans;  
int main(){
    while (cin >> num && num) {
        for (int i = 1; i <= num; i++) {
            scanf_s("%d", &a);
            if (s.empty() || s.top().height < a) {
                temp.height = a;
                temp.index = i;
                s.push(temp);
            }
            else if (!s.empty() && s.top().height >= a) {
                while (!s.empty() && s.top().height >= a) {
                    temp_height = s.top().height;
                    s.pop();
                    if (s.empty()) {
                        length = i - 1;
                    }
                    else {
                        length = i - s.top().index - 1;
                    }
                    ans = max(ans, (long long)length * temp_height);
                }
                temp.height = a;
                temp.index = i;
                s.push(temp);
            }
            if (!s.empty() && i == num) {
                while (!s.empty()) {
                    temp_height = s.top().height;
                    temp_w = s.top().index;
                    if (s.size() == 1) {
                        length = temp_w;
                        s.pop();
                    }
                    else {
                        s.pop();
                        length = temp_w - s.top().index;
                    }
                    ans = max(ans, (long long)length * temp_height);
                }
            }
        }
        printf("%lld\n", ans);
        num = a = length = temp_height = temp_w = 0;
        ans = 0;
        while (!s.empty()) s.pop();
    }
    return 0;
}

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值