Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >计算矩阵中全1子矩阵的个数

计算矩阵中全1子矩阵的个数

作者头像
烟草的香味
发布于 2020-07-15 07:06:57
发布于 2020-07-15 07:06:57
2.8K00
代码可运行
举报
文章被收录于专栏:烟草的香味烟草的香味
运行总次数:0
代码可运行

前言

最近被我大哥安利了一道算法题, 这道题说难, 还不至于我做不出来, 说简单吧, 我还想不到最优解, 等把最优解告诉我之后, 我还正好能理解. 我甚至曾经怯怯的认为, 这题就是我哥专门给我找的, 嘿嘿, 心中说不出的小欢喜.

题来了, 此题出自力扣, 原题链接:

https://leetcode-cn.com/problems/count-submatrices-with-all-ones/

描述: 给你一个只包含 0 和 1 的 rows * columns 矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。

例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
输入: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

题意清晰明了, 开始尝试解题(使用 C 来进行解题).

方案一

首先直观上最先想到的, 就是穷举了. 一力破十会. 将所有出现的情况遍历一遍, 然后就能得出总数了. 思路如下:

  1. 利用i, j 将二维数组的所有节点遍历一遍
  2. 利用m, n将以[i][j]为左上顶点的子矩阵遍历一遍
  3. 判断i, j, m, n四个变量确定的矩阵是否为全1矩阵

代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int numSubmat(int** mat, int matSize, int* matColSize){
    int result = 0;
    // 遍历所有节点
    for (int i = 0; i < matSize; i++) {
        for (int j = 0; j < *matColSize; j++) {
            // 遍历当前节点为左上顶点的所有子矩阵
            for (int m = i; m < matSize; m++) {
                for (int n = j; n < *matColSize; n++) {
                    // 判断当前子矩阵是否为全1矩阵
                    int isOk = 1;
                    for (int p = i; p <= m; p++) {
                        for (int q = j; q <= n; q++) {
                            if(mat[p][q] != 1){
                                isOk = 0;
                                break;
                            }
                        }
                        if(!isOk) break;
                    }
                    // 计算总数
                    if(isOk) result++;
                }
            }
        }
    }
    return result;
}

随手写个测试用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>

int numSubmat(int** mat, int matSize, int* matColSize);

int main() {
    // 定义数组长度
    int matSize = 3, matColSize = 3;
    // 分配数组空间
    int **mat = (int **)malloc(matSize*sizeof(int*));
    // 动态分配内容
    for (int i = 0; i < matSize; i++) {
        mat[i] = (int *)malloc(matColSize*sizeof(int));
    }
    // 给数组填内容, 这里可以接收键盘数组
    mat[0][0] = 1;
    mat[0][1] = 0;
    mat[0][2] = 1;
    mat[1][0] = 1;
    mat[1][1] = 1;
    mat[1][2] = 0;
    mat[2][0] = 1;
    mat[2][1] = 1;
    mat[2][2] = 0;
    int result = numSubmat(mat, matSize, &matColSize);
    printf("%d", result);
    return 0;
}

执行过后, OK, 么的问题. 看一下时间复杂度呢? 一眼就看到了函数里的六层循环, 么的说, O(n^6).

这时, 我大哥说他的时间复杂度是 O(n^3). 那我这小心情, 必须整出来, 再想.

方案二

上面的六层循环中, 能不能想办法去掉一层呢? 有. 在最后判断是否全1的循环中, 如果左上的数字是0, 那必然没有全1子矩阵了

再如果向下找的时候, 碰到0, 那下一列的时候也没必要超过这里了, 因为子矩阵至少有一个0了, 如下图:

image-20200710234204779

在向右遍历的时候同理, 这样, 我们就可以确定, 所有遍历到的值都是1, 可以将判断全1的两层循环去掉. nice. 修改代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int numSubmat(int** mat, int matSize, int* matColSize){
    int result = 0;
    // 遍历所有节点
    for (int i = 0; i < matSize; i++) {
        for (int j = 0; j < *matColSize; j++) {
            if(mat[i][j] == 0) continue;
            int thisMaxColSize = *matColSize; // 当前向右最大值
            // 遍历当前节点为左上顶点的所有子矩阵
            for (int m = i; m < matSize; m++) {
                for (int n = j; n < thisMaxColSize; n++) {
                    if(mat[m][n] == 1) result++;
                    // 记录向右的最大值
                    else thisMaxColSize = n;
                }
            }
        }
    }
    return result;
}

OK, 经过测试完全么的问题. 再看看现在的时间复杂度. O(n^4); 比刚才的六次方, 直接降了两个数量级. 但是比我大哥还差点意思哈.

方案三

打扰了, 没有想到O(n^3)的解法. 经过我哥的一番指点, 可以说是豁然开朗. 思路不变. 上面的四层循环, 有没有什么办法能再减少一层呢?

想一下, 我们在第四层循环中, 向右遍历, 找的是什么? 是连续1的个数, 如果我们不用向右遍历, 直接就知道了这个连续1的个数, 那是不是就可以把这一层也省了呢?

那么问题来了, 如何不遍历就知道呢? 预处理. 在所有的遍历之前, 先进行一次遍历, 把每个节点向右的连续1个数计算好. 这个思路有点妙啊. 废话不多说, 再来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

int min(int a, int b){
    return a > b ? b : a;
}

int numSubmat(int** mat, int matSize, int* matColSize){
    // 进行预处理, 将每个节点向右的连续1个数算好(从右下向左上处理)
    for (int i = matSize - 1; i >= 0; i--) {
        for (int j = *matColSize - 1; j >= 0 ; j--) {
            if(mat[i][j] == 0) continue;
            // 最右侧不处理
            if(j == *matColSize-1) continue;
            // 每个节点的数字等于右边的加1
            mat[i][j] = mat[i][j+1] + 1;
        }
    }
    int result = 0;
    // 遍历所有节点
    for (int i = 0; i < matSize; i++) {
        for (int j = 0; j < *matColSize; j++) {
            if(mat[i][j] == 0) continue;
            int thisMaxColSize = *matColSize; // 当前向右最大值
            // 遍历当前节点为左上顶点的所有子矩阵
            for (int m = i; m < matSize; m++) {
                // 记录向右的最大值
                thisMaxColSize = min(thisMaxColSize, mat[m][j]);
                result += thisMaxColSize;
            }
        }
    }
    return result;
}

再看时间复杂度, 终于, O(n^3).


还有没有比三次方更快的解法呢? 可能..大概..或许有吧. 但是我想了好久也没有想到.

以上, 其实到第二个方案我都想到了, 但是最后一步怎么都没迈出去, 原因归结为做的少, 遇到的少. 算法题偶尔做做还挺好的, 也不需要很高深的数学知识, 还可以锻炼思维, 蛮有趣的, 之后可以抽时间来看看, 嘿嘿.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 烟草的香味 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
统计全为1的子矩形
给你一个只包含 0 和 1 的 rows * columns 矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。
你的益达
2020/08/05
5380
leetcode363. Max Sum of Rectangle No Larger Than K
题目要求 Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix such that its sum is no larger than k. Example: Input: matrix = [[1,0,1],[0,-2,3]], k = 2 Output: 2 Explanation: Because the sum of rectangle [[0, 1]
眯眯眼的猫头鹰
2019/07/02
5640
LeetCode 1504. 统计全 1 子矩形(记录左侧的连续1的个数)
给你一个只包含 0 和 1 的 rows * columns 矩阵 mat , 请你返回有多少个 子矩形 的元素全部都是 1 。
Michael阿明
2020/07/13
8040
最大子矩阵(C/C++)
解决该问题的常用方法是使用动态规划。先计算出每一行的前缀和,然后对于每一列的起始和终止位置,计算出该区域内每一行的和,得到一个一维数组。再对该一维数组使用动态规划求解最大子数组和的问题,得到最大子矩阵的元素之和。
摆烂小白敲代码
2024/09/23
1670
最大子矩阵(C/C++)
【面试高频题】难度 4/5,可逐步优化的超热门面试题
这是 LeetCode 上的「363. 矩形区域不超过 K 的最大数值和」,难度为 「困难」。
宫水三叶的刷题日记
2022/10/30
7240
【面试高频题】难度 4/5,可逐步优化的超热门面试题
力扣240——搜索二维矩阵
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
健程之道
2020/02/12
7210
力扣240——搜索二维矩阵
【算法题目解析】杨氏矩阵数字查找
遇到的一道算法题:已知矩阵内的元素,每行 从左到右递增;每列 从上到下递增;给定一个数字t,要求判断矩阵中是否存在这个元素。
程序员架构进阶
2021/03/05
6680
【算法题目解析】杨氏矩阵数字查找
太难了,有人问了一道刚做的算法题。。。
最近在 LeetCode 的讨论区发现好多同学在求助,因为他们遇到了一些真题,不知道如何处理。
五分钟学算法
2023/10/24
3800
太难了,有人问了一道刚做的算法题。。。
7 道高频面试算法题,你都会了吗?「矩阵 + 位运算 + LRU」
本文将覆盖 「二进制」 + 「位运算」 和 Lru 方面的面试算法题,文中我将给出:
圆号本昊
2021/09/24
1K0
7 道高频面试算法题,你都会了吗?「矩阵 + 位运算 + LRU」
力扣560——和为K的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
健程之道
2020/02/26
4710
动态规划与练习题
动态规划(Dynamic Programming)是分治思想的延伸,通俗一点来说就是大事化小,小事化无的艺术。
二肥是只大懒蓝猫
2023/03/30
3060
动态规划与练习题
LeetCode动画 | 218.天际线问题
今天分享一个LeetCode题,题号是218,标题是天际线问题,题目标签是线段树和Line Sweep [ 扫描线算法 ] ,题目难度是困难。最近新学了Go语言,来尝试一下效果,同时后面也贴出了Java代码【线段树和线扫描】。
我脱下短袖
2020/02/27
1.1K0
【C++】 —— 笔试刷题day_24
这里我们整个二叉树,遍历每一个子树时,我们不仅要拿到其左右子树的高度,而且还要知道左右子树是不是平衡二叉树;
星辰与你
2025/04/25
520
【C++】 —— 笔试刷题day_24
(进阶版)有了四步解题法模板,再也不害怕动态规划!
这次来针对具体的一类动态规划问题,矩阵类动态规划问题,来看看针对这一类问题的思路和注意点。
五分钟学算法
2019/11/18
1.4K0
(进阶版)有了四步解题法模板,再也不害怕动态规划!
剑指offer 13——机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 35, 37 ,因为3+5+3+7=18。但它不能进入方格 35, 38,因为3+5+3+8=19。请问该机器人能够到达多少个格子?
健程之道
2020/05/17
3930
图解LeetCode——1582. 二进制矩阵中的特殊位置(难度:简单)
给你一个大小为 rows * cols 的矩阵 mat,其中 mat[i][j] 是 0 或 1,请返回 矩阵 mat 中特殊位置的数目 。
爪哇缪斯
2023/05/10
2030
图解LeetCode——1582. 二进制矩阵中的特殊位置(难度:简单)
LeetCode 1292. 元素和小于等于阈值的正方形的最大边长(DP)
请你返回元素总和小于或等于阈值的正方形区域的最大边长; 如果没有这样的正方形区域,则返回 0 。
Michael阿明
2020/07/13
7150
LeetCode 85 | 如何从矩阵当中找到数字围成的最大矩形的面积?
今天是LeetCode专题53篇文章,我们一起来看看LeetCode中的85题,Maximal Rectangle(最大面积矩形)。
TechFlow-承志
2020/07/14
1.6K0
LeetCode 1277. 统计全为 1 的正方形子矩阵(DP)
给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。
Michael阿明
2020/07/13
6800
LeetCode 363. 矩形区域不超过 K 的最大数值和(DP+set二分查找)
给定一个非空二维矩阵 matrix 和一个整数 k,找到这个矩阵内部不大于 k 的最大矩形和。
Michael阿明
2020/07/13
9750
推荐阅读
相关推荐
统计全为1的子矩形
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验