LeetCode 1727.重新排列后的最大子矩阵

LeetCode 1727.重新排列后的最大子矩阵

1. 题目描述

给你一个大小为 m x n 的二进制矩阵 matrix ,你可以对矩阵的每一行的列进行任意重新排列(即每一行内部可以任意交换列),但不能改变行之间的相对顺序。请你返回在重新排列后,全为 1 的最大子矩阵的面积。

示例:

复制代码
输入:matrix = [[1,0,1],[1,1,1],[1,1,1]]
输出:6
解释:将第0行的列1和列2交换后,可得到3行2列的全1矩形(高度3,宽度2),面积为6。

2. 解题思路

本题与 LeetCode 85 题「最大矩形」类似,但增加了一个关键条件:每一行的列可以任意重排。这意味着我们不再受限于列原有的顺序,可以自由地将高度较高的列聚在一起,从而构造更大的矩形。

核心思想:

  1. 高度累积 :与85题一样,定义 h[j] 表示从当前行向上(包括当前行)连续 1 的个数。如果当前行某列为 0,则高度清零(连续中断)。
  2. 排序求最优 :由于可以重排列,我们不需要关心列的顺序,只需要知道哪些列的高度足够高。对当前行的高度数组 h 进行升序排序 ,得到 hs
    假设我们想构造一个高度为 h 的矩形,那么所有高度 ≥ h 的列都可以参与。在排序后的数组中,对于每个位置 ihs[i] 是当前值,且它右边的所有列(包括自己)的高度都 ≥ hs[i],因此以 hs[i] 为高的矩形最大可能宽度为 n - in 为列数)。
    遍历所有 i,计算 (n - i) * hs[i] 并更新当前行的最大值。
  3. 逐行计算:对每一行都做上述操作,并取所有行的最大值作为最终答案,因为任何全1矩形必然有底边在某一行。

3. 代码实现

cpp 复制代码
class Solution {
public:
    int largestSubmatrix(vector<vector<int>>& matrix) {
        int n = matrix[0].size();          // 列数
        vector<int> h(n, 0);                // 记录每一列的累积高度
        int res = 0;

        for (auto& row : matrix) {          // 逐行遍历
            // 更新高度数组
            for (int j = 0; j < n; j++) {
                if (row[j] == 1)
                    h[j]++;                  // 连续1,高度+1
                else
                    h[j] = 0;                 // 遇到0,高度清零
            }

            // 对当前行的高度数组进行排序
            auto hs = h;                       // 复制一份(避免破坏原顺序)
            sort(hs.begin(), hs.end());        // 升序排序

            // 遍历排序后的高度,计算可能的最大矩形面积
            for (int i = 0; i < n; i++) {
                // 以 hs[i] 作为矩形的高,宽度为从 i 到末尾的列数
                res = max(res, (n - i) * hs[i]);
            }
        }
        return res;
    }
};

3.1 代码详解

  • 第4行int n = matrix[0].size(); 获取列数。
  • 第5行vector<int> h(n, 0); 初始化高度数组,每列高度初始为0。
  • 第7行:外层循环遍历每一行。
  • 第9-13行:内层循环更新高度。如果当前元素是1,则高度加1;否则高度归零(因为连续1中断)。
  • 第16行 :复制 hhs,因为排序会改变数组顺序,而下一行需要用到原始的 h(累积高度)。
  • 第17行 :对 hs 进行升序排序。排序后,hs[0] 最小,hs[n-1] 最大。
  • 第19-21行 :遍历排序后的数组,以每个 hs[i] 作为矩形的高。由于 hs[i] 是升序的,所以对于下标 i,所有 j >= ihs[j] 都 ≥ hs[i],即至少有 n - i 列的高度不低于 hs[i]。因此,以 hs[i] 为高的矩形最大宽度就是 n - i,面积为 (n - i) * hs[i]。取所有 i 中的最大值作为当前行的最佳矩形面积,并更新全局结果 res
  • 最终返回 res

3.2 示例运行

以题目示例 matrix = [[1,0,1],[1,1,1],[1,1,1]] 为例:

  • 第0行row = [1,0,1]
    更新高度:h = [1,0,1]
    排序后 hs = [0,1,1]
    计算:
    • i=0: (3-0)*0 = 0
    • i=1: (3-1)*1 = 2
    • i=2: (3-2)*1 = 1 → 最大为2
  • 第1行row = [1,1,1]
    更新高度:上一行 [1,0,1],当前行加1 → [2,1,2]
    排序后 hs = [1,2,2]
    计算:
    • i=0: 3*1 = 3
    • i=1: 2*2 = 4
    • i=2: 1*2 = 2 → 最大为4
  • 第2行row = [1,1,1]
    更新高度:[3,2,3]
    排序后 hs = [2,3,3]
    计算:
    • i=0: 3*2 = 6
    • i=1: 2*3 = 6
    • i=2: 1*3 = 3 → 最大为6

最终结果 res = max(2,4,6) = 6

4. 复杂度分析

  • 时间复杂度 :O(m * n log n)
    • 每行更新高度 O(n)
    • 每行排序 O(n log n)
    • 总共有 m 行,因此总复杂度 O(m * n log n)
      (如果使用计数排序可以优化到 O(m * n),但本题 n ≤ 10^5 且元素为 0/1,排序的 log n 可接受)
  • 空间复杂度 :O(n)
    只需要一维数组 h 和临时数组 hs(排序需要额外 O(n) 空间,但可以就地排序,不过为了保持原数组我们复制了一份,所以额外 O(n))。

5. 与 LeetCode 84/85 的对比

题目 特点 核心算法 关键差异
84 一维柱状图,不可重排 单调栈 找左右边界
85 二维01矩阵,不可重排 逐行累积高度 + 84解法 转化为一维,单调栈
1727 二维01矩阵,可重排列 逐行累积高度 + 排序 利用重排特性,排序后取最大宽度

相同点 :都使用了高度累积 的技巧,将二维问题转化为一维问题。
不同点:85题不能重排,因此需要用单调栈找到实际的左右边界;1727题可以重排,所以排序后直接取最宽的列数即可。

6. 总结

本题的关键在于理解"重排列"带来的便利:我们只需关注每一列的高度,而无需关心它们的原始顺序。通过累积高度并对每行排序,可以轻松求出以当前行为底边的最大可能矩形面积。这种思想在涉及列重排的矩阵问题中非常有用,值得掌握。

相关推荐
自信150413057592 小时前
数据结构之二叉树算法题
c语言·数据结构·算法
qianbo_insist2 小时前
鱼眼图像的三维投影逆变换和AI计算
人工智能·opencv·算法
我怎么又饿了呀2 小时前
DataWhale—大模型的算法基础(文本表示与词向量)
算法
珠海西格电力2 小时前
5G+物联网,零碳园区管理系统的“信息高速路”
大数据·人工智能·物联网·算法·5g
Frostnova丶2 小时前
LeetCode 84 & 85.柱状图最大矩形与最大矩形
算法·leetcode
We་ct2 小时前
LeetCode 427. 建立四叉树:递归思想的经典应用
前端·算法·leetcode·typescript·dfs·深度优先遍历·分治
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- 补充包】
开发语言·前端·数据结构·数据库·c++·算法·蓝桥杯
夏乌_Wx2 小时前
Linux 进程间通信 IPC 总结:管道 + 信号量 + 共享内存 + 消息队列(附代码)
linux·数据结构·算法