题目描述:
给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例 1:
输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“0”]]
输出:6
解释:最大矩形如上图所示。
示例 2:
输入:matrix = []
输出:0
示例 3:
输入:matrix = [[“0”]]
输出:0
示例 4:
输入:matrix = [[“1”]]
输出:1
示例 5:
输入:matrix = [[“0”,“0”]]
输出:0
提示:
rows == matrix.length
cols == matrix[0].length
1 <= row, cols <= 200
matrix[i][j] 为 ‘0’ 或 ‘1’
分析
很有意思的一道题,本题相当于leetcode 84. 柱状图中最大的矩形这题的拓展应用。
首先我们要求矩阵中全为1的子矩阵中最大矩形的面积。暴力做法是枚举矩形的左上角和右下角坐标来确定一个矩形,时间复杂度是O(n4),至于判断这个矩形里是不是全1,使用二维前缀和就可以在O(1)的时间内判断出来。显然暴力的做法会TLE,n的规模是200,如果我们能将复杂度降低到立方级别,就可以解决本题了。既然枚举矩形的两个顶点会超时,那么我们优化的方向就可以是只去枚举一个顶点,这和在线性DP里面我们往往只需要求以某个元素为末尾的最优解,而不用去枚举左右端点来求最优解一样。因此本题我们可以只去枚举矩形的右下角顶点,用f[i][j]表示以第i行第j列元素为矩形的右下角的矩形中最大的面积。
随便举个例子:
0 1 1 1 1
0 1 0 1 1
1 1 1 1 1
我们以上面矩阵的右下角端点为矩形的右下角端点,尝试来枚举能够构成的矩形的宽度,可以发现最后一行有5个连续的1,但是并不能延伸到倒数第二行,因此宽度为5的矩形最大面积就是5,。再来增加矩形的高度,向上枚举,由于第二行最右边只有两个连续的1,所以最后一行只有两个1能继续往上延伸,新的宽度就是2,一直可以延伸到第一行,这样宽度是2的矩形的最大面积就是2 * 3 等于6了。
观察下上面的枚举过程,可以发现,在枚举以matrix[i][j]为矩形右下角的矩形时,宽度能够向左延伸到多少取决于matrix[i][j]左边有多少个连续的1,而向上增加矩形的高度时,能够向上继续延伸的宽度取决左边1最少的那一行,比如最后一层有5个1,由于第二层最右边只有2个1,矩形的宽度就不得不缩减到2,再往上如果最后一列左边的1继续减少的话,矩形的宽度还会继续减少,这其中一直要使用的一个属性就是矩形中某个元素及其左边有多少个1.
如果预处理出来w[i][j]为matrix[i][j]及在第i行第j列左边连续1的个数,则状态转移方程为w[i][j] = w[i][j-1] + 1,matrix[i][j] = 1时;w[i][j] = 0,matrix[i][j] = 0时。有了w[i][j]这个属性,我们枚举以matrix[i][j]为右下顶点的矩形时,就只需要向上去枚举矩形的高度了,因为宽度为已经枚举行的w的最小值。这样整体算法的复杂度就降低到了立方级别。
观察上图,我们之前的算法在枚举最后一列时,以第三行最后一列为矩形右下顶点的最大矩形面积等于max(5,2*3);以第二行最后一列为右下顶点的最大矩形面积等于2 * 2;以第一行最后一列为右下顶点的最大矩形面积等于4。这一列为右下角能够构成的矩形的最大面积恰好就是上图所绘的直方图中最大矩形的面积,也就是我们求一列元素构成矩形右下角的矩形中最大的面积时,就只需要求以这列元素为横坐标,其左边1的个数为纵坐标的直方图中矩形的最大面积,也就是开头所说的那题一模一样的问题,使用单调栈就可以将一列最大矩形的面积的枚举时间复杂度优化到线性,总的复杂度是平方级别的。求直方图的最大面积可以参考AcWing 131 直方图中最大的矩形这篇题解。
代码
O(n3) 模拟解法
class Solution {
public:
int w[205][205] = {0};
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size(),n = matrix[0].size();
for(int i = 0;i < m;i++){
w[i][0] = (matrix[i][0] == '1');
for(int j = 1;j < n;j++){
if(matrix[i][j] == '1') w[i][j] = w[i][j-1] + 1;
}
}
int res = 0;
for(int i = 0;i < m;i++){
for(int j = 0;j < n;j++){
int wid = w[i][j];
for(int k = i;k >= 0&&wid;k--){
wid = min(wid,w[k][j]);
res = max(res,wid*(i - k + 1));
}
}
}
return res;
}
};
O(n2) 单调栈解法
class Solution {
public:
int w[205][205] = {0};
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size(),n = matrix[0].size();
for(int i = 0;i < m;i++){
w[i][0] = (matrix[i][0] == '1');
for(int j = 1;j < n;j++){
if(matrix[i][j] == '1') w[i][j] = w[i][j-1] + 1;
}
}
int res = 0;
for(int j = 0;j < n;j++){
vector<int> stk;
vector<int> l(m+1,0);
stk.push_back(-1);
w[m][j] = -1;
for(int i = 0;i <= m;i++){
while(stk.size() > 1 && w[i][j] < w[stk.back()][j]){
int u = stk.back();
res = max(res,w[u][j]*(i - l[u] - 1));
stk.pop_back();
}
l[i] = stk.back();
stk.push_back(i);
}
}
return res;
}
};