Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.
参考链接:http://www.cnblogs.com/TenosDoIt/p/3454877.html
思路:
一般一个题目我首先会想想怎么暴力解决,比如这一题,可以枚举出所有的矩形,求出其中的面积最大者,那么怎么枚举呢,如果分别枚举矩形的宽度和高度,这样还得枚举矩形的位置,复杂度至少为O(n^4) (计算复杂度是我们把matrix的行、列长度都泛化为n,下同),我们可以枚举矩形左上角的位置,那么知道了矩形左上角的位置,怎么计算以某一点为左上角的矩形的最大面积呢?举例如下,下面的矩阵我们以(0,0)为矩形的左上角:
1 1 1 1 0 0
1 1 1 0 1 1
1 0 1 0 1 1
0 1 1 1 1 1
1 1 1 1 1 1
矩形高度是1时,宽度为第一行中从第一个位置起连续的1的个数,为4,面积为4 * 1 = 4
矩形高度是2时,第二行从第一个位置起连续1的个数是3,宽度为min(3,4) = 3,面积为3*2 = 6
矩形高度为3时,第三行从第一个位置起连续1的个数是1,宽度为min(1,3) = 1,面积为1*3 = 3
矩形高度为4时,第四行从第一个位置起连续1的个数是0,宽度为min(0,1) = 0,面积为0*4 = 0
后面的行就不用计算了,因为上一行计算的宽度是0,下面所有宽度都是0
因此以(0,0)为左上角的矩形的最大面积是6,计算以某一点为左上角的矩形的最大面积复杂度是O(n)。
注意到上面我们用到了信息“从某一行某个位置开始连续的1的个数”,这个我们可以通过动态规划求得:设dp[i][j]是从点(i,j)开始,这一行连续1的个数,动态规划方程如下:
- 初始条件:dp[i][column-1] = (matrix[i][column-1] == '1') (column是matrix的列数)
- dp[i][j] = (matrix[i][j] == '1') ? 1 + dp[i][j + 1] : 0 ,(从方程看出我们应该从每一行的后往前计算)
计算dp复杂度O(n^2),枚举左上角位置以及计算以该位置为左上角的矩形最大面积复杂度是O(n^2*n)=O(n^3),总的复杂度是O(n^3)
这个算法还可以优化,枚举到某个点时我们可以假设该点右下方全是1,得到一个假设最大面积,如果这个面积比当前计算好的面积还要小,该点就可以直接跳过;在上面计算以某点为左上角的矩形面积时,也可以剪枝,剪枝方法同上。具体可以参考代码注释。
代码:
class Solution {
public:
int maximalRectangle(vector<vector<char> > &matrix) {
int row=matrix.size();
if(row==0) return 0;
int column=matrix[0].size();
int dp[row][column],res=0;
memset(dp,0,sizeof(dp));
for(int i=0;i<row;i++) dp[i][column-1]=(matrix[i][column-1]=='1');
for(int i=0;i<row;i++){
for(int j=column-2;j>=0;j--){
if(matrix[i][j]=='1'){
dp[i][j]=1+dp[i][j+1];
}
}
}
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
if((column-j)*(row-i)<=res) break;
int width=dp[i][j];
for(int k=i;k<row && width>0;k++){
if(width*(row-i)<=res) break;
if(width>dp[k][j]) width=dp[k][j];
res=max(res,width*(k-i+1));
}
}
}
return res;
}
};