Java 中计算所有元素的子矩阵

2025年5月12日 | 阅读 8 分钟

计算全为 1 的子矩阵数量是在编程中一个常见的问题,它涉及到在给定的 二进制 矩阵(仅包含 0 和 1)中,找到所有元素都为 1 的子矩阵的数量。这个问题广泛应用于图像处理、数据分析和优化问题。给定一个 m x n 的二进制矩阵,我们的任务是计算所有可能的只包含 1 的子矩阵。

示例

示例 1

输入

矩阵 =

1 1

1 1

输出:全为 1 的子矩阵总数:9

解释

在这种情况下,给定的矩阵由 1 组成,允许我们形成多个子矩阵。我们可以计算每个单独的 1x1 子矩阵,这贡献了四个。此外,我们可以形成 1x2 的水平子矩阵和 2x1 的垂直子矩阵,每个子矩阵出现两次。最后,整个 2x2 子矩阵也是有效的。将所有可能的子矩阵相加,我们得到 4 + 2 + 2 + 1 = 9。

示例 2

输入

矩阵 =

1 0 1

1 1 1

1 1 1

输出:全为 1 的子矩阵总数:24

解释

对于这个 3x3 的矩阵,所有元素都是 1,这意味着我们可以形成大量的有效子矩阵。我们从计算所有 1x1 的子矩阵开始,这为总数贡献了 **9**。转向稍大的子矩阵,1x2 的水平子矩阵和 2x1 的垂直子矩阵各贡献 6,因为它们可以形成在不同的位置。接下来,可以计算 2x2 的子矩阵,贡献 4 个。此外,1x3 的水平子矩阵和 3x1 的垂直子矩阵各出现 3 次。最后,整个 3x3 的矩阵本身也算作一个有效的子矩阵,为总数增加一个。将所有可能的子矩阵相加,我们得到 24。

示例 3

输入

矩阵 =

1 1 1 1

1 1 1 1

输出:全为 1 的子矩阵总数:30

解释

2x4 的矩阵只包含 1,允许形成许多可能的子矩阵。首先,每个 1x1 的子矩阵贡献 8 个。转向更大的水平子矩阵,1x2 的子矩阵出现 6 次,而 1x3 的子矩阵出现 4 次,1x4 的子矩阵出现 2 次。

由于矩阵有两行,因此垂直子矩阵也需要考虑,2x1 的子矩阵出现 4 次。此外,2x2 的子矩阵可以以 3 种不同的方式形成,2x3 的子矩阵可以以 2 种方式形成,最后,整个 2x4 的矩阵本身是一个有效的子矩阵,为总数增加一个。将所有可能的子矩阵加在一起,我们得到总共 30 个。

使用暴力方法

算法

步骤 1:首先确定给定二进制矩阵的行数和列数。初始化一个变量来跟踪只包含 1 的子矩阵的数量。

步骤 2:使用嵌套循环迭代所有可能的起始位置 (i, j),它们代表潜在子矩阵的左上角。

步骤 3:对于每个选择的左上角,然后迭代所有可能的结束位置 (x, y),它们代表子矩阵的右下角。

步骤 4:提取由 (i, j) 作为左上角和 (x, y) 作为右下角定义的子矩阵的元素,并检查此范围内的所有元素是否都为 1。

步骤 5:如果在验证过程中在子矩阵中找到 0,则将其丢弃并移动到下一个可能的子矩阵。否则,增加计数,因为这是一个有效的子矩阵。

步骤 6:继续此过程,处理给定矩阵中的所有可能子矩阵,确保考虑所有有效的配置。

步骤 7:所有迭代完成后,返回只包含 1 的有效子矩阵的总数。

实施

输出

 
Example 1 Output: 9
Example 2 Output: 24
Example 3 Output: 30   

复杂度分析

时间复杂度

在最坏的情况下,时间复杂度为 O(n⁶),因为它涉及迭代所有可能的左上角 O(n²),所有可能的右下角 O(n²),并在此处检查每个子矩阵是否全为 1,这可能需要 O(n²) 的操作,导致 O(n²) * O(n²) * O(n²) = O(n⁶)。

空间复杂度

空间复杂度为 O(1),因为该算法仅使用几个 整数 变量,并且除了输入矩阵之外不需要额外的内存。

使用递归

算法

步骤 1:确定给定二进制矩阵的行数和列数,并初始化一个计数器来跟踪有效子矩阵。

步骤 2:通过循环遍历矩阵中的每个元素来迭代所有可能的子矩阵左上角。

步骤 3:从每个左上角扩展到所有可能的右下角,这有效地生成了每个可能的子矩阵。

步骤 4:对于每个子矩阵,通过迭代其元素来检查它是否只包含 1。

步骤 5:如果在验证过程中发现 0,则丢弃该子矩阵并移至下一个;否则,增加计数器。

步骤 6:重复此过程,直到检查完所有可能的子矩阵,并确保考虑所有有效的配置。

步骤 7:检查完所有子矩阵后,返回最终计数。

实施

输出

 
Example 1 Output: 9
Example 2 Output: 24
Example 3 Output: 30   

复杂度分析

时间复杂度

在最坏的情况下,时间复杂度为 O(n⁴),因为选择所有左上角需要 O(n²),选择右下角需要 O(n²),检查子矩阵需要 O(n²),从而得到 O(n⁴)。

空间复杂度

空间复杂度为 O(1),因为只使用了几个整数变量,而没有额外的 数据结构

使用动态规划

算法

步骤 1:创建一个 二维数组 来存储每个单元格结束的子矩阵数量,并初始化一个变量来跟踪总计数。

步骤 2:遍历矩阵中的每个单元格。如果单元格的值为 1,则使用先前行和列的值计算以该位置结束的子矩阵的数量。

步骤 3:如果单元格位于第一行和第一列,则将其计为一个子矩阵。

步骤 4:如果单元格位于第一行,则将先前列的计数加到当前计数上,因为它只能水平扩展。

步骤 5:如果单元格位于第一列,则将先前行的计数加到当前计数上,因为它只能垂直扩展。

步骤 6:对于其他位置,通过考虑来自上方单元格、左侧单元格和对角相邻单元格的贡献来计算子矩阵,同时避免重复计数。

步骤 7:将为每个单元格计算的子矩阵累加到总计数中,并在处理完所有元素后返回最终计数。

实施

输出

 
Example 1 Output: 9
Example 2 Output: 24
Example 3 Output: 30   

复杂度分析

时间复杂度

此方法的 time complexity 为 O(m × n),因为矩阵中的每个单元格都只处理一次,其值根据先前计算的相邻单元格的值来确定。不需要对子矩阵进行嵌套迭代,这使其比暴力方法效率更高。

空间复杂度

space complexity 为 O(m × n),因为使用了一个与输入矩阵大小相同的额外二维数组来存储中间结果。它允许高效的计算,但需要与矩阵维度成比例的额外内存。