Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 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 0Return 4.
做完这题最大的收获就是加深了对动态规划省空间的理解。
参考这里的解法点击打开链接
题目要求求面积, 面积是由周长决定的,首先建立一个二维数组,int【】【】dp ,dp[i][j]的值对应的是matrix【i】【j】位置上所对应的点能构成的最大正方形的边长,
对应上图得到的dp【】【】结果为
1 0 1 0 0 1 0 1 1 1 1 1 1 2 2 1 0 0 1 0用一个变量track dp【】【】的最大值 2, 得到最大面积为 2 * 2.
matrix的第一行和第一列是可以初始化对应dp二维数组的,如果有1的话, 则可以构成边长为1的正方形, matrx中的0对应到dp都是0,
对于 i > 1, j > 1(假设都在界内), 则能否构成正方形要看 当前元素 matrix[i][j] 的左边节点, 上边节点, 和左上角的节点, 如果这三个点中有一个是0, 而当前点是1, 则dp[i][j] = 1, 如果这三个点都不为0, 则dp[i][j] = math.min(dp[i][j - 1],dp[i - 1][j], dp[i - 1][j -1]) + 1. 因为边长又多了1, 这个公式对于三点中有0,当前值是1同样适用。
对应代码:
public class Solution { public int maximalSquare(char[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0].length == 0){ return 0; } int[][] dp = new int[matrix.length][matrix[0].length]; int maxLen = 0; for(int i = 0; i < matrix.length; i++){ dp[i][0] = Character.getNumericValue(matrix[i][0]); if(dp[i][0] > maxLen){ maxLen = dp[i][0]; } } for(int i = 0; i < matrix[0].length; i++){ dp[0][i] = Character.getNumericValue(matrix[0][i]); if(dp[0][i] > maxLen){ maxLen = dp[0][i]; } } for(int i = 1; i < matrix.length; i++){ for(int j = 1; j < matrix[0].length; j++){ if(matrix[i][j] == '0'){ dp[i][j] = 0; }else{ // dp[i][j] = Math.min(Math.min(Character.getNumericValue(matrix[i][j - 1]), Character.getNumericValue(matrix[i - 1][j])), Character.getNumericValue(matrix[i - 1][j - 1])) + 1; dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1; if(dp[i][j] > maxLen){ maxLen = dp[i][j]; } } } } return maxLen * maxLen; } }
然后想到优化这个dp, 因为你每次更新dp值的时候只需要知道当前行前一列的值, 前一行的当前值, 以及前一行的前一列的值, 即
dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]所以用两个一位数组分别记录当前行和前一行的值就可以了。
</pre></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 10px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;">对应代码:</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 10px; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 30px;"><pre name="code" class="java">public class Solution { public int maximalSquare(char[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0].length == 0){ return 0; } int[] dp = new int[matrix[0].length]; int maxLen = 0; // for(int i = 0; i < matrix.length; i++){ // dp[i][0] = Character.getNumericValue(matrix[i][0]); // if(dp[i][0] > maxLen){ // maxLen = dp[i][0]; // } // } // int tmpPre = 0; 本来是想用一个变量来存储 top-left 的值, 其实可以用另一个二维数组来存上一排的信息, 这样两个一位数组就可以了, 也是一种空间的优化。 int[] pre = new int[matrix[0].length]; for(int i = 0; i < matrix[0].length; i++){ dp[i] = Character.getNumericValue(matrix[0][i]); pre[i] = dp[i]; if(dp[i] > maxLen){ maxLen = dp[i]; } } for(int i = 1; i < matrix.length; i++){ for(int j = 0; j < matrix[0].length; j++){ if(j == 0){ dp[j] = Character.getNumericValue(matrix[i][j]); if(dp[j] > maxLen){ maxLen = dp[j]; } }else{ if(matrix[i][j] == '0'){ dp[j] = 0; }else{ dp[j] = Math.min(Math.min(dp[j], dp[j - 1]), pre[j - 1]) + 1; if(dp[j] > maxLen){ maxLen = dp[j]; } } } } // set curRow dp[] as pre[], so in the next row we still have the info of the cur row. for(int k = 0; k < dp.length; k++){ pre[k] = dp[k]; } } return maxLen * maxLen; } }
想了很久怎么用一个变量来代替 pre这个一位数组, 其实每次对新的一行扫描时, 把dp【j】用一个变量tmp存起来, 然后到当前行下一列的时候这个tmp就是dp【i - 1】【j - 1】了, 因为在整个一行的for loop 进行之前, dp【j】中存的值都是上一行的值,
代码如下:
public class Solution { public int maximalSquare(char[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0].length == 0){ return 0; } int[] dp = new int[matrix[0].length]; int maxLen = 0; int lastPre = 0; for(int i = 0; i < matrix[0].length; i++){ dp[i] = Character.getNumericValue(matrix[0][i]); if(dp[i] > maxLen){ maxLen = dp[i]; } } for(int i = 1; i < matrix.length; i++){ for(int j = 0; j < matrix[0].length; j++){ int tmp = dp[j]; if(j == 0){ dp[j] = Character.getNumericValue(matrix[i][j]); if(dp[j] > maxLen){ maxLen = dp[j]; } }else{ if(matrix[i][j] == '0'){ dp[j] = 0; }else{ dp[j] = Math.min(Math.min(dp[j], dp[j - 1]), lastPre) + 1; if(dp[j] > maxLen){ maxLen = dp[j]; } } } lastPre = tmp; } } return maxLen * maxLen; } }