📜  全为1的子矩阵数(1)

📅  最后修改于: 2023-12-03 15:36:45.787000             🧑  作者: Mango

全为1的子矩阵数
介绍

在一个二维01矩阵中,全为1的子矩阵是指连续的若干行和若干列组成的矩阵,其中所有的元素都为1。给定一个矩阵,计算其中全为1的子矩阵的数量。

解决方法

暴力法

一个直接的想法是,对于矩阵中的每一个元素,以它为左上角,向右和向下扩展,计算以此为左上角的全为1的子矩阵数量。但是这种方法时间复杂度为O(n^6),无法通过大规模数据。

动态规划

动态规划可以使得时间复杂度降为O(n^3)。定义dp[i][j]表示以第i行第j列为右下角的全为1的子矩阵的数量,那么状态转移方程为:

dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + 1 (matrix[i][j] == 1) dp[i][j] = 0 (matrix[i][j] == 0)

解释一下:以第i行第j列为右下角的全为1的子矩阵,可以由以下三种情况得到:

  1. 以第(i-1)行第j列为右下角的全为1的子矩阵向下扩展一行得到;
  2. 以第i行第(j-1)列为右下角的全为1的子矩阵向右扩展一列得到;
  3. 以第(i-1)行第(j-1)列为右下角的全为1的子矩阵向右向下扩展一行一列得到。

但是这样会算重复,所以要把以第(i-1)行第(j-1)列为右下角的全为1的子矩阵的数量减去。

时间复杂度:O(n^3)

单调栈

单调栈可以使得时间复杂度降为O(n^2)。维护一个height数组,height[j]表示第i行第j列上方连续为1的个数。那么问题就转化为了给定一个直方图,计算其中全为1的子矩阵的数量。

我们枚举每一行i,把这一行作为底,然后将height数组看做一个直方图,利用单调栈可以求出以这一行为底的最大全为1子矩阵的面积。这个面积就是以第i行第j列为右下角的全为1的子矩阵的数量。

时间复杂度:O(n^2)

代码实现

动态规划

def countSubmat(matrix):
    m, n = len(matrix), len(matrix[0])
    dp = [[0] * n for _ in range(m)]
    res = 0
    for i in range(m):
        for j in range(n):
            if matrix[i][j] == 1:
                dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + 1
                res += dp[i][j]
    return res

单调栈

def countSubmat(matrix):
    m, n = len(matrix), len(matrix[0])
    height = [0] * n
    res = 0
    for i in range(m):
        for j in range(n):
            if matrix[i][j] == 0:
                height[j] = 0
            else:
                height[j] += 1
            k = j
            while k >= 0 and height[k] > 0:
                res += height[k]
                k -= 1
    return res
小结

全为1的子矩阵数是一个有趣的问题,它有多种解决方法,其中动态规划和单调栈都是比较好的解决方法。这个问题的解决方法也可以应用到其他问题中,如最大全为1的子矩阵面积、最大全为0的子矩阵面积等。