您的位置:首页 > 其它

221. Maximal Square

2016-08-18 05:55 375 查看
Given a 2D binary matrix filled with 0’s and 1’s, find the largest

square containing only 1’s and return its area.

For example, given the following matrix:

> 1 0 1 0 0
> 1 0 1 1 1
> 1 1 1 1 1
> 1 0 0 1 0


Return 4

这题跟之前的maximal rectangle乍看是一样的。差别在于一个求最大矩形面积一个求正方形面积。看清楚单词square和rectangle。在rectangle那道题中比较好的做法有预处理后建立新的表格检索处理方法,也有结合一维数组中的最大矩形面积做法的写法。具体的两种方法讨论见链接里另一篇博客。

这里可以直接套用预处理建立表格的方法来做。

方法一:预处理建立表格

对于给出矩阵,建立相同size的数组矩阵,每一位数大小对应从它开始向右检索的连续1的个数。建立矩阵后,依次检索,对于每一个不是0的位置,讨论以它为左上角对应的最大square面积。代码如下:

int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
if (m == 0) return 0;
int n = matrix[0].size();
vector<vector<int>> res(m, vector<int>(n+1, 0));
int i, j, k;
//预处理建立表格,每个数代表以此向右连续的1个数
for (i = 0; i < m; i++) {
for (j = n-1; j >=0; j--) {
if (matrix[i][j] == '1') res[i][j] = res[i][j+1] + 1;
else res[i][j] = 0;
}
}
int maxarea = 0, length = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++) {
//增加预判,如果最大可能都没有,直接跳过
if (res[i][j]*res[i][j] <= maxarea) continue;
//该点向下检索,每步更新最小长度,约束k
else {
length = res[i][j];
for (k = 1; k <= length && (i+k-1) < m; k++) {
length = min(length, res[i+k-1][j]);
if (length < k) break;
//一旦检索行不符合要求,终止
}
//记录此时能达到的最大行数,也就是正方形边长
length = k - 1;
maxarea = max(maxarea, length*length);
}
}
}
return maxarea;
}


这种方法跟maximal rectangle的那种做法如出一辙,给我们提了个预处理的方法。但这题更好更快的做法还是dynamic programming. 因为是正方形,形状固定,之前的矩形有太多可能性,没有直接的递推方法,但正方形可以。

方法二:动态规划dynamic programming

基本的递推关系可以表示如下,state equation:

P[0][j] = matrix[0][j] (topmost row);
P[i][0] = matrix[i][0] (leftmost column);
For i > 0 and j > 0:
if matrix[i][j] = 0, P[i][j] = 0;
if matrix[i][j] = 1, P[i][j] = min(P[i - 1][j], P[i][j - 1], P[i - 1][j - 1]) + 1.


这就有点像之前的棋盘里的各种路径问题了,初始化第一行第一列,然后依次递推。建立的动态表格里,每一个数代表了以这个位置为右下角的最大正方形面积。基本代码如下:

int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
if (m == 0) return 0;
int n = matrix[0].size();
vector<vector<int>> res(m, vector<int>(n, 0));
int i, j, k, length = 0;
//初始化第一列
for (i = 0; i < m; i++) {
res[i][0] = matrix[i][0] - 48<
b3bb
/span>;
length = max(length, res[i][0]);
}
//初始化第一行
for (j = 0; j < n; j++) {
res[0][j] = matrix[0][j] - 48;
length = max(length, res[0][j]);
}
//依次递推
for (i = 1; i < m; i++) {
for (j = 1; j < n; j++) {
if (matrix[i][j] == '1') {
res[i][j] = min(res[i-1][j-1], min(res[i-1][j], res[i][j-1])) + 1;
length = max(length, res[i][j]);
}
else res[i][j] = 0;
}
}
return length*length;
}


优化一:

当然这种动态规划的题,一般都可以在基础表述上优化,一般也都是在优化内存使用问题。因为需要用的位置只有三个,只占用两列,所以我们可以构造两列数组,滚动存储。代码如下:

int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
if (m == 0) return 0;
int n = matrix[0].size();
vector<int> prev(m, 0);
vector<int> cur(m, 0);
int i, j, length = 0;
//初始化第一列
for (i = 0; i < m; i++) {
prev[i] = matrix[i][0] - 48;
//注意一定要在边界就更新最大长度,因为可能矩阵里只有边界有1
length = max(length, prev[i]);
}
for (j = 1; j < n; j++) {
//先初始化第一个元素,也是第一行的部分,为递推做准备
cur[0] = matrix[0][j] - 48;
length = max(length, cur[0]);
for (i = 1; i < m; i++) {
if (matrix[i][j] == '1') {
cur[i] = min(cur[i-1], min(prev[i-1], prev[i])) + 1;
length = max(length, cur[i]);
}
else cur[i] = 0;
}
swap(prev, cur);
//更新prev,并且将cur归零
fill(cur.begin(), cur.end(), 0);
}
return length*length;
}


优化二:

还可以将内存使用降低到一列!因为如果我们只用一列,那么在更新这一列的时候,这个位置cur[i]原来的值其实就是prev[i], 也就是下一个位置需要的prev[i-1]。再加上cur[i-1]。所以我们用一列也可以记录三个位置的信息,具体代码如下:

int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.empty()) return 0;
int m = matrix.size(), n = matrix[0].size();
vector<int> dp(m + 1, 0);
//因为没有初始化动作,所以长度增加一位,方便直接对第一行的元素计算
int maxsize = 0, pre = 0;
for (int j = 0; j < n; j++) {
for (int i = 1; i <= m; i++) {
//将这个位置更新前信息保存,因为这是下一个位置的prev[i-1],之后便赋值给pre。
int temp = dp[i];
//因为dp最一开始就是全0,所以对第一列并不影响
if (matrix[i - 1][j] == '1') {
dp[i] = min(dp[i], min(dp[i - 1], pre)) + 1;
maxsize = max(maxsize, dp[i]);
}
else dp[i] = 0;
pre = temp;
}
}
return maxsize * maxsize;
}


这里对dynamic programming的使用对比之前的maximal rectangle来说很有启发,内存上的优化值得大家学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: