leetcode
leetcode 1551 ~ 1600
重新排列后的最大子矩阵

重新排列后的最大子矩阵

难度:

标签:

题目描述

给你一个二进制矩阵 matrix ,它的大小为 m x n ,你可以将 matrix 中的  按任意顺序重新排列。

请你返回最优方案下将 matrix 重新排列后,全是 1 的子矩阵面积。

 

示例 1:

输入:matrix = [[0,0,1],[1,1,1],[1,0,1]]
输出:4
解释:你可以按照上图方式重新排列矩阵的每一列。
最大的全 1 子矩阵是上图中加粗的部分,面积为 4 。

示例 2:

输入:matrix = [[1,0,1,0,1]]
输出:3
解释:你可以按照上图方式重新排列矩阵的每一列。
最大的全 1 子矩阵是上图中加粗的部分,面积为 3 。

示例 3:

输入:matrix = [[1,1,0],[1,0,1]]
输出:2
解释:由于你只能整列整列重新排布,所以没有比面积为 2 更大的全 1 子矩形。

示例 4:

输入:matrix = [[0,0],[0,0]]
输出:0
解释:由于矩阵中没有 1 ,没有任何全 1 的子矩阵,所以面积为 0 。

 

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m * n <= 105
  • matrix[i][j] 要么是 0 ,要么是 1

代码结果

运行时间: 156 ms, 内存: 37.5 MB


/*
 * 使用Java Stream的方式解决问题:
 * 1. 计算每一列的高度。
 * 2. 对每一行排序后计算面积。
 * 3. 使用Stream API实现数组的处理。
 */
import java.util.Arrays;
import java.util.Comparator;

public class Solution {
    public int largestSubmatrix(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        int maxArea = 0;
        // 计算每一列的高度
        for (int i = 1; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 1) {
                    matrix[i][j] += matrix[i - 1][j];
                }
            }
        }
        // 对每一行进行排序并计算面积
        for (int i = 0; i < m; i++) {
            int[] sortedRow = Arrays.stream(matrix[i])
                                     .boxed()
                                     .sorted(Comparator.reverseOrder())
                                     .mapToInt(Integer::intValue)
                                     .toArray();
            for (int j = 0; j < n; j++) {
                maxArea = Math.max(maxArea, sortedRow[j] * (j + 1));
            }
        }
        return maxArea;
    }
}

解释

方法:

这个题解的思路是首先计算每个元素在当前列上可达的最大行数,这可以通过从下到上遍历实现。然后对每一行的数列进行排序,并计算可能形成的最大矩形面积,这一步是利用排序后的列值来确定最宽的可能矩形。主要步骤包括:1. 初始化一个与给定矩阵同样大小的矩阵 a,用于记录每个元素上方连续1的数量(包括自身);2. 从下到上计算矩阵 a 的值;3. 对每一行的列值进行降序排序,并计算每个可能的宽度对应的最大面积。

时间复杂度:

O(m * n log n)

空间复杂度:

O(m * n)

代码细节讲解

🦆
在计算每个元素上方连续1的数量时,为什么要从下到上遍历,而不是从上到下遍历?
从下到上遍历使得我们在处理当前行时,下一行(即较低的行)的连续1的数量已经算好,这样可以直接使用这个信息来更新当前行的状态。如果从上到下遍历,因为我们尚未计算下一行的连续1的数量,就无法直接更新当前行,这会使算法更加复杂或者需要更多的状态来记录信息。
🦆
如何理解题解中的操作`r2[j] = r3[j] if r1[j] else k`?这里的逻辑是如何确保连续性的?
这条代码的意思是如果当前行的第 j 列是 1 (`r1[j]`是真),那么当前元素的上方连续1的数量(不包括当前行)就等于下一行同列元素的上方连续1的数量(`r3[j]`),否则连续性被打断,应重新计数(这里使用`k`,但根据题解的上下文,这似乎是一个错误,理应是设置为0)。这种方式确保了只有在当前列元素为1且上一行同列也是1的情况下,连续性才被保持。
🦆
在处理矩阵a之后进行的降序排序,为什么选择降序而不是升序?这对最终计算的面积有何影响?
降序排序是为了使每行的列值按从大到小排列,这样在计算每个可能的宽度对应的最大矩形面积时,可以直接取用最大的连续1的数量。如果使用升序,我们需要从列表的末尾开始计算,这在逻辑上更复杂且不直观。通过降序,我们可以保证在宽度为 j 时,所使用的行高都是当前可能的最大值,从而最大化矩形的面积。
🦆
题解中提到的条件`(m - i) * n <= ans`用于中断循环,这个判断条件是如何得出的?它是基于什么逻辑或数学推导?
这个条件是基于面积的最大可能估计来设置的。`(m - i) * n`表示从当前行到矩阵底部的所有行构成的最大可能矩形区域的面积(假设全部是1)。如果这个值已经小于或等于当前已知的最大矩形面积`ans`,那么即使在之后的行中找到了更大的连续1的数量,也无法构成更大的矩形面积,所以可以提前终止循环,节省计算资源。

相关问题