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

题目: 

给你一个 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));

    }
}

测试结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(2024|ICLR,MAD,真实数据与合成数据,自吞噬循环)自消耗生成模型变得疯狂

Self-Consuming Generative Models Go MAD 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 自吞噬生成模型 2.1 自吞噬过程 2.2 自吞噬过程的变体 2.3 自吞噬循环中的偏…

Advanced EFS Data Recovery:恢复 Windows NTFS 中 EFS 加密文件

Advanced EFS Data Recovery 数据恢复软件可以破解 NTFS 加密&#xff0c;并解密受 Windows 加密文件系统 &#xff08;EFS&#xff09; 保护的文件。 Advanced EFS Data Recovery 功能列表 通过破解加密文件系统 &#xff08;EFS&#xff09; 来解除 NTFS 加密 解密转移到另…

Java面试题(11)

59.说一说springMVC的运行流程 1. 用户向服务器发送请求&#xff0c;请求被 Spring 前端控制 Servelt DispatcherServlet 捕获&#xff1b; 2. DispatcherServlet 对请求 URL 进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;。然后根据该 URI&#xff0c;…

STM32 E18-D80NK红外避障传感器

E18-D80NK-N是一款红外光电传感器&#xff0c;它同时具备发射和接收功能。通过对发射光进行调制后发出&#xff0c;并通过接收头对反射光进行解调输出。 E18-D80NK-N采用了透镜来增强传感器的性能&#xff0c;使其能够检测更远的距离。根据红外光的特性&#xff0c;不同颜色的…

VitePress-04-文档中的表情符号的使用

说明 vitepress 的文档中是支持使用表情符号的&#xff0c;像 &#x1f602; 等常用的表情都是支持的。 本文就来介绍它的使用方式。 使用语法 语法 &#xff1a; :表情名称: 例如 &#xff1a; :joy: &#x1f602; 使用案例代码 # 体会【表情】的基本使用 > hello world …

22.Lambda 表达式

Lambda 表达式 1. 概况2. 函数式接口3. 格式3.1 完整格式3.2 省略格式 4. 代码示例5. 输出结果6. 注意事项 学习Lambda表达式之前最好先学会 匿名内部类 具体信息请查看 API 帮助文档 1. 概况 Lambda 表达式是一种在编程中用来表示匿名函数的简洁语法。它是基于函数式编程风格…

2024.1.27每日一题

LeetCode 最大合金数 2861. 最大合金数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设你是一家合金制造公司的老板&#xff0c;你的公司使用多种金属来制造合金。现在共有 n 种不同类型的金属可以使用&#xff0c;并且你可以使用 k 台机器来制造合金。每台机器都需…

SpringBoot之JWT登录

JWT JSON Web Token&#xff08;JSON Web令牌&#xff09; 是一个开放标准(rfc7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任&#xff0c;因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…

嵌入式学习第十三天

9.指针: &#xff08;1&#xff09;const指针 const 关键字 常量(只读) 1.const int *p; 2.int const *p; 1和2是等价的 const修饰 *p,指针变量p的值可以改变,但不能利用指针修改指向空间中的值 3.int *const p; const修饰 p,指针变量p的值不能改变…

【教程】极简Docker搭建“帕鲁幻兽PalWorld”服务器, 附资源

注意&#xff1a; 如果搭建在个人服务器或者内网中&#xff0c;需要做内网穿透&#xff0c;可以看这篇博客&#xff1a; 【教程】超详细安装和使用免费内网穿透软件Zerotier-One-CSDN博客文章浏览阅读523次&#xff0c;点赞8次&#xff0c;收藏8次。真的很详细https://blog.csd…

[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

Mysql查询数据

1 基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。SELECT语句的基本格式是&#xff1a; 2 单表查询 2.1 查询所有字段 SELECT * FROM 表名; 2.2 在SELECT语句中指定所有字段 SELECT f_id, s_id ,f_name, f_price FROM fruits; 2.3 查询单个字段 SELECT 列名FR…

Ajax入门与使用

目录 ◆ AJAX 概念和 axios 使用 什么是 AJAX&#xff1f; 怎么发送 AJAX 请求&#xff1f; 如何使用axios axios 函数的基本结构 axios 函数的使用场景 1 没有参数的情况 2 使用params参数传参的情况 3 使用data参数来处理请求体的数据 4 上传图片等二进制的情况…

C数据类型

目录 1. 数据类型分类 2. 整数类型 3. 浮点类型 4. void 类型 5. 类型转换 1. 数据类型分类 在 C 语言中&#xff0c;数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间&#xff0c;以及如何解释存储的位模式。 C 中…

格外空间以设计带动凯迪仕品牌价值增长 | 揽获6项国际设计大奖

Kaadas凯迪仕专注于智能锁领域&#xff0c;作为一家集产品研发、制造、品牌、全球销售、安装、售后于一体的全产业链公司&#xff0c;致力于服务全球每一个家庭&#xff0c;以更优质的产品&#xff0c;为全球众多家庭带去高品质生活体验。基于新消费时代背景&#xff0c;传统空…

联想懂的通信×实在智能:共同探索智连融合AI创新发展路径

近日&#xff0c;联想集团副总裁/联想懂的通信CEO王帅、CFO周利军、COO&CPO邢海洋、CGO赵晨、CTO边毅等领导一行莅临杭州实在智能科技有限公司开展研讨座谈。 实在智能创始人&CEO孙林君、联合创始人&COO高扬、联合创始人&CMO张俊九、销售VP&运营商事业线负…

成都直播产业园核心优势全面解读,入驻天府锋巢直播产业基地都有哪些好处?

一文讲清&#xff01;成都直播产业园核心优势全面解读 企业入驻天府锋巢直播产业基地能获得哪些好处&#xff1f; 锋巢资讯&#xff5e;又来了&#xff5e;&#xff5e;&#xff5e; 今天&#xff0c;将为您全面解读成都产业园重点特色产业服务&#xff08;上&#xff09; 什…

vit细粒度图像分类(五)TransFC学习笔记

1.摘要 细粒度图像具有不同子类间差异小、相同子类内差异大的特点。现有网络模型在处理过程中存在特征提取能力不足、特征表示冗余和归纳偏置能力弱等问题&#xff0c;因此提出一种改进的 Transformer图像分类模型。 首先&#xff0c;利用外部注意力取代原 Transformer模型中的…

Java之Idea中创建Web项目

一、新建动态web项目 1、新建项目 2、选择创建动态web项目 3、项目命名 4、编辑index.jsp 二、配置Tomcat 1、新增tomcat服务器配置 2、选择服务器类型 3、配置服务器参数 4、部署项目 5、完成配置 6、启动运行 7、访问web项目 在浏览器地址栏输入&#xff1a; http://local…

RSTP的P/A机制

如图所示根桥S1和S2之间新添加了一条链路,在当前状态下S2的另外几个端口p2是Alternate端口,p3是指定端口且处于Forwarding状态,p4是边缘端口。新链路连接成功后,P/A机制协商过程如下。 1.P0和P1两个端口马上都先成为指定端口发送RS TBPDU。 2.S2的P1口收到更优的RST BPD…