[单调栈] aw3516. 最大面积(单调栈+最大矩形面积进阶+今日头条2021+好题)

1. 题目来源

链接:84. 柱状图中最大的矩形
链接:131. 直方图中最大的矩形

相关题解:

2. 题目解析

字节 2021 年 5 月 9 日笔试题。


做这道题前,得先掌握 单调栈,再把 131. 直方图中最大的矩形 搞了,再把 [单调栈] aw152. 城市游戏(单调栈+最大矩形面积进阶)
搞了,再来做这道题。

其实大同小异,最优解出现在 (x, y) 点的 上下左右,四个部分,这四个部分都可以抽象成 [单调栈] aw152. 城市游戏(单调栈+最大矩形面积进阶) 这个题,故预处理得到上下左右四个方向的最大矩形面积,然后针对每个查询进行查表即可。

直接计算每一个查询的最大面积,时间复杂度为 O ( n 2 ∗ Q ) O(n^2*Q) O(n2Q),优化预处理后,就将 Q Q Q,优化到 O ( 1 ) O(1) O(1) 了。故时间复杂度为 O ( n 2 ) O(n^2) O(n2),完全可以 AC 本题。
在这里插入图片描述

注意:

  • 本题在抽象 下左右,的时候值得注意一下这种处理方式,尤其是 左右 部分的时候,压缩连续 1 的个数的时候,循环顺序从 行—列,要变为 列—行。这也是很棒的处理方式
  • 在计算最大矩形面积的时候,只需要数组从 1 开始,数组有效长度即可。在这一步是封装出来的。

本题可以多写几次,很棒的题目。


时间复杂度 O ( n 2 ) O(n^2) O(n2)
空间复杂度 O ( n 2 ) O(n^2) O(n2)


单调栈标准写法:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1005;

int n, m;
char a[N][N];
int h[N][N], l[N], r[N];
int stk[N], tt;

int main() {
    scanf("%d%d", &n, &m);
    
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin >> a[i][j];             // 空格隔开,采用 cin 来读,不要使用 scanf,会读入空格
    
    for (int i = 1; i <= n; i ++ ) 
        for (int j = 1; j <= m; j ++ ) {
            if (a[i][j] == 'F') 
                h[i][j] = h[i - 1][j] + 1;
        }
    
    int res = 0;
    for (int i = 1; i <= n; i ++ ) {
        tt = 0;
        for (int j = 1; j <= m; j ++ ) {
            while (tt && h[i][stk[tt]] >= h[i][j]) tt -- ;
            if (tt) l[j] = stk[tt];
            else l[j] = 0;		// 左边界是 0
            stk[++tt] = j;
        }
        tt = 0;
        for (int j = m; j; j -- ) {
            while (tt && h[i][stk[tt]] >= h[i][j]) tt -- ;
            if (tt) r[j] = stk[tt];
            else r[j] = m + 1;
            stk[++tt] = j;
        }
        
        for (int j = 1; j <= m; j ++ ) res = max(res, h[i][j] * (r[j] - l[j] - 1));
    }
    printf("%d\n", 3 * res);
    return 0;
}

简洁的写法,设置哨兵,避免空栈判断:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1005;

int n, m;
int h[N][N], l[N], r[N];
int stk[N];

int work(int h[]) {
    int tt = 0;
    h[0] = h[m + 1] = -1;
    stk[0] = 0;					// 设置哨兵,栈永远不空
    for (int i = 1; i <= m; i ++ ) {
        while (h[stk[tt]] >= h[i]) tt -- ;
        l[i] = stk[tt];
        stk[++tt] = i;
    }
    
    tt = 0;
    stk[0] = m + 1;				// 设置哨兵,栈永远不空
    for (int i = m; i; i -- ) {
        while (h[stk[tt]] >= h[i]) tt -- ;
        r[i] = stk[tt];
        stk[++tt] = i;
    }
    
    int res = 0;
    for (int i = 1; i <= m; i ++ ) res = max(res, h[i] * (r[i] - l[i] - 1));
    return res;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ ) {
            char c;
            cin >> c;
            if (c == 'F')
                h[i][j] = h[i - 1][j] + 1;
        }

    int res = 0;
    for (int i = 1; i <= m; i ++ ) res = max(res, work(h[i]));
    cout << res * 3 << endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值