算法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));}
}

测试结果

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT初体验——让他写代码,走起

⭐️ 前言 今天注册了Google账户&#xff0c;并通过Gmail体验了一下ChatGPT3.5&#xff0c;让他帮小编写了几个python程序&#xff0c;而且不用任何改动便可以运行&#xff0c;666&#xff0c;感觉很爽。 体验ChatGPT3.5很简单&#xff0c;只要能科学上网和Google账户就行&am…

【Linux C | 进程】Linux 进程间通信的10种方式(1)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【STM32】快速搭建工程

1. 快速生成工程 2.下载DFP包或者根据已有DFP包安装 3.拷贝现有工程的操作系统&#xff0c;应用层代码 debug 就完成最新工程的快速搭建 4.编译发现如下图 5.修改完这个报错&#xff0c;新工程建立基本完成&#xff0c;如果有其他错误&#xff0c;缝缝补补就可 比如 Main.c文…

Linux中并发程序设计(进程的创建和回收、exec函数使用、守护进程创建和使用、GDB的父、子进程代码的调试、线程的创建和参数传递)

进程的创建和回收 进程概念 概念 程序 存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09; 静态的 进程 执行一个程序所分配的资源的总称 动态的进程和程序比较 注&#xff1a;进程是存在RAM中&#xff0c;程序是存放在ROM(flash)中的进程内容 BSS段&#xff…

使用QT实现播放gstreamer的命令(二)

一、前言 上一篇文章写到了&#xff0c;如何快速使用C来执行gstreamer的命令&#xff0c;如何在QT中显示gstreamer的画面&#xff0c;原文如下&#xff1a; https://blog.csdn.net/Alon1787/article/details/135107958 二、近期的其他发现&#xff1a; 1.gstreamer的画面显示在…

树--二叉树(C语言纯手凹)

目录 1.什么是树&#xff1f;&#xff08;不深入&#xff0c;仅做了解&#xff09; 2.树的表示方式 2.1孩子兄弟表示法&#xff08;左孩子右兄弟&#xff09; 2.2孩子表示法 2.3双亲表示法 3.什么是二叉树 4.二叉树分类 4.1满二叉树 4.2完全二叉树 4.3二叉搜索树&#x…

优质硬盘检测工具SMART Utility,保障您的Mac数据安全

在日常使用Mac电脑的过程中&#xff0c;我们经常会存储大量的重要数据&#xff0c;如照片、文档、视频等。然而&#xff0c;硬盘故障却是一件令人头疼的事情&#xff0c;可能会导致数据丢失、系统崩溃等严重后果。为了保障您的数据安全&#xff0c;我们推荐一款专业的硬盘检测工…

Springboot自定义线程池实现多线程任务

1. 在启动类添加EnableAsync注解 2.自定义线程池 package com.bt.springboot.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTask…

案例分析技巧-软件工程

一、考试情况 需求分析&#xff08;※※※※&#xff09;面向对象设计&#xff08;※※&#xff09; 二、结构化需求分析 数据流图 数据流图的平衡原则 数据流图的答题技巧 利用数据平衡原则&#xff0c;比如顶层图的输入输出应与0层图一致补充实体 人物角色&#xff1a;客户、…

Spring中用Mybatis注解查询映射多个对象

1.映射写法如下 SelectProvider(type UserGroupMapper.class, method "getOrigins")Results({Result(property "id", column "id"),Result(property "groupId", column "groupId"),Result(property "resId&qu…

Windows系统本地安装Everything搜索神器并结合内网穿透实现远程访问

文章目录 前言1.软件安装完成后&#xff0c;打开Everything2.登录cpolar官网 设置空白数据隧道3.将空白数据隧道与本地Everything软件结合起来总结 前言 要搭建一个在线资料库&#xff0c;我们需要两个软件的支持&#xff0c;分别是cpolar&#xff08;用于搭建内网穿透数据隧道…

【gcc】RtpTransportControllerSend学习笔记 5:OnTransportPacketsFeedback及测试程序

【gcc】RtpTransportControllerSend学习笔记 4:码率分配 中阅读了大神的文章,依旧云雾中,因此,反复阅读大神的文章,结合代码,继续。本次是 基于m98的代码。src\modules\congestion_controller\goog_cc\goog_cc_network_control.cc GoogCcNetworkController 是核心类,其处…