[CP / LeetCode] 85. Maximal Rectangle - 最大矩形
解法一:二维最大子段和
分析
自己想出来的解法,但凡出题人把输入的规模改大一点就通过不了。
做这道题的时候我拼命在想它和前一道题(84. Largest Rectangle in Histogram - 柱状图中最大的矩形)的关系。如果每一列的 '1' 都是连续分布的,且 “贴牢地面”,那么本题就退化为了前一道题……可惜并不是这样。
由于本题的目标是找到 “最大” 的全 '1' 矩形,而且目前看下来没有什么能 “直接” 计算出最大值的办法,换言之,需要枚举所有可能的矩形,然后求面积,取最大值。
最暴力的方式是枚举矩形的两个端点,然后看看构成的矩形面积是否和矩形内 '1' 的数量相等,时间复杂度为
这样的时间复杂度显然是无法接受的。对于满足要求的矩形来说,它的每一列 / 每一行必定为全 '1',如果能利用此性质来构造目标矩形,便能缩小搜索范围,进而降低时间复杂度。
我联想起了以前做过的一道二维最大子段和题,它的时间复杂度是
首先需要计算出
我们可以花费
如果是,则有
假设某个矩形位于第
而枚举所有的行-行组合需要
所以总的时间复杂度为
——但从学习的角度来说还不够,或者说,从数据中提取的互信息还不够多,所以请看解法二。
代码 -
可以优化成
,虽然并没有什么用
class Solution { public: int maximalRectangle(vector<vector<char>>& matrix) { int row = matrix.size(), col = matrix[0].size(); std::vector<std::vector<int>> pre(row + 1); for (auto& c : pre) { c.resize(col + 1); } for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { pre[i][j] = pre[i - 1][j] + matrix[i - 1][j - 1] - '0'; } } int ans = 0; for (int i = 1; i <= row; i++) { for (int j = i; j <= row; j++) { int h = j - i + 1, t = 0, tm = 0; for (int k = 1; k <= col; k++) { if (pre[j][k] - pre[i - 1][k] == h) { ++t; } else { tm = std::max(tm, t); t = 0; } } tm = std::max(tm, t); ans = std::max(ans, tm * h); } } return ans; } };
运行结果
解法二:单调栈
分析
根据对称性,猜测理想的时间复杂度符合
如果去掉一层
……我承认这一做法不太容易讲明白。不过幸运的是,对于此方法,你能在题解区里找到好多清晰透彻的解释,因此请容许我在这里结尾吧。
代码 - -
class Solution { public: int maximalRectangle(vector<vector<char>>& matrix) { int row = matrix.size(), col = matrix[0].size(), ans = 0; std::vector<std::vector<int>> pre(row + 1); for (auto& c : pre) { c.resize(col + 1); } for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { pre[i][j] = pre[i - 1][j] + matrix[i - 1][j - 1] - '0'; if (matrix[i - 1][j - 1] == '0') { pre[i][j] = 0; } } } for (int i = 1; i <= row; i++) { std::stack<int> s; s.push(col + 1); // sentinel for (int j = col; j >= 1; j--) { while (!s.empty()) { int sTop = s.top(); if (sTop == col + 1 ? 0 : pre[i][sTop] >= pre[i][j]) { s.pop(); ans = std::max(ans, pre[i][sTop] * (s.top() - j - 1)); } else { break; } } s.push(j); } int n = s.size(); for (int k = 0; k < n - 1; k++) { int sTop = s.top(); s.pop(); ans = std::max(ans, pre[i][sTop] * (s.top() - 1)); } } return ans; } };
运行结果
结尾的碎碎念
这是我解决的第 3 道困难题。虽然每道题都要思考数个小时之久,但是做出来的那一刻所带来的巨大成就感真是让人久久回味不能忘怀。我准备好接受更多挑战了!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)