算法39:统计全 1 子矩形(力扣1504)----单调栈

news2025/2/22 17:58:05

题目: 

给你一个 m x n 的二进制矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。

示例 1:

输入:mat = [[1,0,1],[1,1,0],[1,1,0]]
输出:13
解释:
6 个 1x1 的矩形。
有 2 个 1x2 的矩形。
有 3 个 2x1 的矩形。
有 1 个 2x2 的矩形。
有 1 个 3x1 的矩形。
矩形数目总共 = 6 + 2 + 3 + 1 + 1 = 13 。

示例 2:

输入:mat = [[0,1,1,0],[0,1,1,1],[1,1,1,0]]
输出:24
解释:8 个 1x1 的子矩形。
有 5 个 1x2 的子矩形。
有 2 个 1x3 的子矩形。
有 4 个 2x1 的子矩形。
有 2 个 2x2 的子矩形。
有 2 个 3x1 的子矩形。
有 1 个 3x2 的子矩形。
矩形数目总共 = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24

这一题比较有意思,我花费了一周左右的时间才绕出来。下面来说一下我的解题思路。

1. 假设 数组为 1 0 1, 我们知道它只有2个矩形

2 .那么如果数组变成了:

        1 0 1

        1 1 0.

我们知道第一行只有2个,

第二行可以是下标为0的{1}, 下标为1的{1}, 下标0和1的组合{1,1} 因此累计是3个; 但是,我们还发现第一行和第二行的第一列也可以组成一个矩形。 因此,第二行累计多出了4个矩形

3. 假设数组再增加一行:

此时,第三行单独新增了3个矩形。

但是第三行和第二行组合:

即第二行和第三行的第一列组成一个矩形;

第二行和第三行的第二列组成一个矩形;

第二行和第三行的第一列与第二列也可以组成一个矩形;

不仅如此,第一行、第二行、第三行的第一列也可以组成一个矩阵;

因此,第三行新增的矩阵个数为(3+1+1+1+1)= 7个。

总矩形数量就是 2 + 4 + 7 = 13个。

单调栈:单调栈结构可以快速的找到任意一个数左、右侧比自己大(或小)的数字的下标。

我们按照单调栈的思想,把以上的数组再推导一遍。此时,数组是:

1 0 1

1 1 0

1 1 0

第一行 2个矩形

数组压缩以后,第二行变成 2 1 0.

高度为2只有1个矩形;

高度为1,可以得到下标0和1.  (2-1)* (2 * (2+1)/2 )= 3个

因此第二行累计是 1 + 3 = 4个矩形。

第三行数组压缩以后变成 3 2 0

高度为3的只有1个矩形

高度为2,2*(2*(2+1)/2)= 6个

因此,这个数组累计矩形为 2 + 4 + (1+6)= 13 个矩形

如果还不理解,我再举个例子

假设数组为 1 1 1, 我们根据公式可得  3 * (3+1)/2 = 6;

假设再增加一行:

1 1 1

1 1 1

第一行是6个;

第二行是 3 * (3+1)/2 = 6;但是,第一行和第二行联合起来,还可以拼 3 * (3+1)/2 = 6; 也就是说第二行实际新增了 2*6= 12;

假设再增加一行:

1 1 1

1 1 1

1 1 1

那第一行是 1* 3 * (3+1)/2 = 6;

第二行是     2* 3 * (3+1)/2 = 12;

第三行就是 3 *  3 * (3+1)/2 = 18个;

1对应1行,2对应2行,3对应3行。

现在用压缩数组的角度再来看:

第一行 1 1 1.  根据 1* 3 * (3+1)/2 = 6

第二行变成了 2 2 2. 根据 2* 3 * (3+1)/2 = 12

第三行变成了 3 3 3 根据 3* 3 * (3+1)/2 = 18

有没有发现,公式前面的 1 2 3和压缩数组的数组元素高度出奇的一致?

现在把数组变化一下,左侧是原始数组,右侧是进行数组压缩后的结果

1 1 1 0   ===  1 1 1 0

1 1 1 0  ==== 2 2 2 0

1 1 1 0  ==== 3 3 3 0

1 1 1 1 ==== 4 4 4 1

那第一行是 1* 3 * (3+1)/2 = 6;

第二行是     2* 3 * (3+1)/2 = 12;

第三行就是 3 *  3 * (3+1)/2 = 18个;

第四行就要分情况了:

首先:以高度为4的情况,可得 3 * (3+1)/2 = 6

其次,以高度为3的情况,可得 3 * (3+1)/2 = 6

然后,以高度为2的情况, 可得  3 * (3+1)/2 = 6

最后,以1为高度的情况,注意,此时以高度为1的情况长度为4,  4 *(4+1)/2 = 10;

总的概括,第四行就是前三行的组合:

第四行与前三行组合不就是 (4-1)*(3 * (3+1)/2 )= 3* 6 = 18个矩形

第四行单独新增   (1-0)* (4 *(4+1)/2 = 10;

因此,最终矩形数量为 : 6 + 12 +  18 + (6+6+6+10)= 64个矩形;

package code04.单调栈_01;

/**
 * 力扣1504, 统计全1矩阵
 * https://leetcode.com/problems/count-submatrices-with-all-ones
 */
public class Code05_SumOfRectangleForAllOne {

    public int numSubmat(int[][] mat)
    {
        if (mat == null || mat.length == 0) {
            return 0;
        }
        int sum = 0;
        int[] help = new int[mat[0].length];
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat[i].length; j++) {
                //数组压缩
                help[j] = mat[i][j] == 0 ? 0 : help[j] + 1;
            }
            sum += countRectangle(help);
        }

        return sum;
    }

    public int countRectangle(int[] help)
    {
        int size = help.length;
        //自定义栈结构
        int[] stack = new int[size];
        int stackSize = 0;

        int ans = 0;
        for (int i = 0; i < size; i++)
        {
            //如果栈中元素比当前数组i对应的数据大,弹出栈中数据
            while (stackSize != 0 && help[stack[stackSize-1]] > help[i]) {
                //弹出栈顶元素
                int cur = stack[--stackSize];
                //左侧比弹出的cur小的位置
                int left = stackSize == 0 ? -1 : stack[stackSize -1];
                //确保单调栈的连通性,获取左、有两侧比当前cur小的值中较大的数
                int max = Math.max(left == -1 ? 0 : help[left],  help[i]);
                //统计cur作为最小值的范围
                int quantity = i - 1 - left;


                /**
                 * help[cur] - max 代表高度中高出的部分. 比如
                 * 1 0 1 中有2个矩形
                 *
                 * 再增加一行
                 * 1 0 1   = 2个
                 * 1 1 0   = 4个
                 * 此时数组压缩成了2 1 0
                 * 此时的 help[cur] - max就代表 2 - 1. 即高度为2的部分单独算
                 * 而count(quantity) 就代表高度为2的连续元素有多少个
                 *
                 * 根据压缩后的数组 2, 1, 0,推导第二行矩形个数
                 * 先以高度为2的计算 (2-1) * (1*(1+1)/2) = 1个
                 * 再以高度为1的计算 (1-0) * (2*(2+1)/2) = 3个
                 * 合计 1+3 =4个
                 *
                 *
                 * 如果在增加一行
                 *  1 0 1      = 2 个矩形
                 *  1 1 0      = 4 个矩形
                 *  1 1 1      = 10 个矩形
                 *  最后一行数组压缩成 3 2 1
                 *  先算高度为3的 (3 - 2)* (1*(1+1)/2) = 1个
                 *  再算高度为2的 (2 - 1) * (2*(2+1)/2) = 3个
                 *  最后算高度为1的 (1-0) * (3*(3+1)/2) = 6个
                 *  合计 1+ 3 + 6 = 10个
                 *
                 * 那么,如果数组为
                 *  1 0 1
                 *  1 1 0
                 *  1 1 1
                 *
                 *  那么,总的矩形就是 2 + 4 + 10 = 16个
                 *
                 */
                ans += (help[cur] - max) * count(quantity);
            }
            stack[stackSize++] = i;
        }

        while (stackSize != 0) {
            //弹出栈顶元素
            int cur = stack[--stackSize];
            //左侧比弹出的cur小的位置
            int left = stackSize == 0 ? -1 : stack[stackSize -1];
            //确保单调栈的连通性
            int max = Math.max(left == -1 ? 0 : help[left], 0);
            //统计cur作为最小值的范围
            int quantity = size - 1 - left;

            ans +=  (help[cur] - max)  * count(quantity);
        }
        return ans;
    }

    public int count (int n) {
        return n * (n+1) >> 1;
    }

    public static void main(String[] args) {
        Code05_SumOfRectangleForAllOne ss = new Code05_SumOfRectangleForAllOne();
        int[][] mat = {
            {1,0,1},
            {1,1,0},
            {1,1,1}
        };
        System.out.println(ss.numSubmat(mat));

    }
}

测试结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418832.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

申万宏源基于 StarRocks 构建实时数仓

作者 &#xff1a;申万宏源证券 实时数仓项目组 小编导读&#xff1a; 申万宏源证券有限公司是由新中国第一家股份制证券公司——申银万国证券股份有限公司与国内资本市场第一家上市证券公司——宏源证券股份有限公司&#xff0c;于 2015 年 1 月 16 日合并组建而成&#xff0c…

error: failed to open index: Database already open. Cannot acquire lock报错解决办法

ordinals节点数据同步出现报错 error: failed to open index: Database already open. Cannot acquire lock.问题分析&#xff1a; 出现问题的原因是btcoin core节点数据没有同步完我们就开始进行ordinals数据同步&#xff0c;导致/root/.local/share/ord/index.redb 文件数据…

盒子模型的内容总结

知识引入 1.认识盒子模型 在浏览网站时我们会发现内容都是按照区域划分的。这使得网页很工整、美观。在页面中&#xff0c;每一块区域分别承载不同的内容&#xff0c;使得网页的内容虽然零散&#xff0c;但是在版式排列上依然清晰有条理。如图1 图1 *承载内容的区域称为盒子…

Next.js 学习笔记(八)——优化

优化 Next.js 提供了多种内置优化&#xff0c;旨在提高应用程序的速度和核心网络生命指数。本指南将介绍可用于增强用户体验的优化功能。 内置组件 内置组件抽象化了实现常见 UI 优化的复杂性。这些组件包括&#xff1a; 图像&#xff1a;基于本地 <img> 元素构建。图…

Flink问题解决及性能调优-【Flink rocksDB读写state大对象导致背压问题调优】

RocksDB是Flink中用于持久化状态的默认后端&#xff0c;它提供了高性能和可靠的状态存储。然而&#xff0c;当处理大型状态并频繁读写时&#xff0c;可能会导致背压问题&#xff0c;因为RocksDB需要从磁盘读取和写入数据&#xff0c;而这可能成为瓶颈。 遇到的问题 Flink开发…

STM32 有源蜂鸣器

模块介绍: 结构&#xff1a;有源蜂鸣器通常由一个振膜和一个驱动电路组成。振膜是负责产生声音的部分&#xff0c;而驱动电路则负责控制振荡频率和幅度。 工作原理&#xff1a;有源蜂鸣器的驱动电路会向振膜施加电压&#xff0c;使其振动产生声音。驱动电路可以根据输入信号的…

centos7安装mysql5.7 或者mysql8

1、centos7安装mysql8 mysql官网 https://dev.mysql.com/downloads/mysql/ 示例2个版本的下载地址 #5.7.30下载地址 wget https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.30-1.el7.x86_64.rpm-bundle.tar #8.0.22下载地址 wget https://cdn.mysql.com/archives/mysql-8…

网络防御保护——课程笔记

一.防火墙 防火墙的主要职责&#xff1a;控制和防护 --- 安全策略 --- 防火墙可以根据安全策略来抓取流量之后做出对应的动作。 防火墙的分类 防火墙的发展进程 防火墙的控制 带内管理 --- 通过网络环境对设备进行控制 --- telnet&#xff0c;ssh&#xff0c;web --- 登录设备…

【node】Node.js的常用内置模块:

文章目录 一、os模块&#xff1a;【1】常用的OS模块方法包括&#xff1a;【2】案例&#xff1a; 二、path模块&#xff1a;【1】常用的path模块方法包括&#xff1a;【2】案例&#xff1a; 三、url模块&#xff1a;【1】常用的url模块方法包括&#xff1a;【2】案例&#xff1a…

【Apache POI】百万级数据导出Excel,并含有折线等图表

需求概要 最近接到一个需求&#xff0c;概要来讲就是实现百万级数据导出Excel&#xff0c;并根据其中的数据项自动生成折线图等图表。经技术调研&#xff0c;针对内存、性能等要素&#xff0c;Apache POI此技术可完成此需求。 Apache POI是Apache软件基金会的开放源码函式库&am…

《教我兄弟学Android逆向15 xpose改机开发03-写一款自己的改机软件》

上一篇 《教我兄弟学Android逆向14 xpose改机开发02-改机代码基础编写》我带你熟悉了一下改机的流程,搭建了改机的基础代码,改机参数数据储存方面我们用的是SharedPreferences存储数据,界面方面写了一个一键新机的按钮,点一键新机的时候会随机生成imei数据存储到xml数据中,然后…

isctf---re

crackme 解压得到crackme.exe 运行得到flag babyRe 先猜e65537的rsa 先用Z3强行求出p、q&#xff0c;算出常规rsa中的phi&#xff0c;然后套用公式求出m exp #babyre wp from z3 import * import libnum from Crypto.Util.number import * p,q,cInts(p q c) S Solver() S…

【前端web入门第一天】02 HTML图片标签 超链接标签 音频标签 视频标签

文章目录: 1.HTML图片标签 1.1 图像标签-基本使用1.2 图像标签-属性1.3 路径 1.3.1 相对路径 1.3.2 绝对路径 2.超链接标签 3.音频标签 4.视频标签 1.HTML图片标签 1.1 图像标签-基本使用 作用:在网页中插入图片。 <img src"图片的URL">src用于指定图像…

前端框架---Vue2学习教程(上)

从HTML到现在一路跟过来的小伙伴们&#xff0c;坚持固然不容易&#xff0c;但我相信大家已经学到了不少&#xff0c;那么我们开始马不停蹄的进入前端的框架吧&#xff0c;下面讲的是Vue2&#xff0c;大家继续加油鸭&#xff01;&#xff01;&#xff01;&#xff01; Vue2 Vu…

遗传算法优化最大化效应的某些需求点可不配送的vrptw问题

标题&#xff1a;遗传算法优化最大化效应的某些需求点可不配送的vrptw问题 摘要&#xff1a; 在可不配送的车辆路径配送问题&#xff08;VRPTW&#xff09;中&#xff0c;我们面临着优化路径规划以最大化效用的挑战。本文提出了一种基于遗传算法的方法&#xff0c;旨在解决具…

【Shell实战案例面试题】输入网卡的名字,来输出网卡的IP

1.问题 参数后判断要加"" 名字为空时显示ip 2.分析 把本机的所有网卡名列出来&#xff0c;来引导用户输入 使用命令列出所有网卡信:ifconfig/ip a 设计一个函数&#xff0c;把网卡名作为参数&#xff0c;函数返回网卡的IP 在获取某个网卡IP时&#xff0c;考虑网…

Redis面试(三)

1.Redis报内存不足怎么处理 Redis内存不足的集中处理方式&#xff1a; 修改配置文件redis.cof的maxmemory参数&#xff0c;增加Redis的可用内存通过命令修改set maxmemory动态设置内存上限修改内存淘汰策略&#xff0c;及时释放内存使用Redis集群&#xff0c;及时进行扩容 2…

基于springboot的美发管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

让Unity迭代器性能提升5倍

最近在研究Unity il2cpp的代码生成和编译优化&#xff0c;结合之前遇到过的一个优化案例&#xff0c;给大家讲讲在Unity中迭代器相关代码生成的底层原理&#xff0c;以及在写代码过程中需要注意的一些特殊情况。 案例 首先我们来看一个非常简单的案例&#xff0c;代码如下&am…

3338 蓝桥杯 wyz的数组IV 简单

3338 蓝桥杯 wyz的数组IV 简单 //C风格解法1&#xff0c;通过率50% #include<bits/stdc.h>int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int n; std::cin >> n;int ans 0;std::vector<int>a(n);for(auto &am…