【动态规划】LeetCode221.最大正方形-Medium

129 阅读2分钟

题目

在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。 示例 1:

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4

示例 2:

输入:matrix = [["0","1"],["1","0"]]
输出:1

示例 3:

输入:matrix = [["0"]]
输出:0

动态规划四步走

1. 定义状态
2. 初始状态
3. 状态转移方程
4. 从dp[]中获取结果

具体到题目

定义状态

定义dp[][],其中dp[i][j]表示到matrix[i][j]的最大正方形的边长

初始状态

dp[0][0] = matrix[0][0]; 行和列的初始化都比较简单,碰到1之后,后面的都是1

状态转移方程

对于dp[i][j],需要判断左边dp[i][j-1]和上面dp[i-1][j]综合得到。 如果matrix[i][j] === 0,dp[i][j] 为dp[i][j-1]和dp[i-1][j]的较大值。 如果matrix[i][j] === 1,dp[i][j] 可能比dp[i][j-1]和dp[i-1][j]的最大值还大1,并且同时需要小于i和j,假设p = Math.max(dp[i][j-1],dp[i-1][j])+1 (p <= i && p <= j)只需要遍历是matrix[i-p][j-p]到matrix[i][j]是否全为1既可,若满足,则dp[i][j]=p,否则,p[i][j]为dp[i][j-1]和dp[i-1][j]的较大值。

if matrix[i][j] === 0
	dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
else
	p = Math.max(dp[i][j-1],dp[i-1][j])+1 (p <= i && p <= j)
   	if matrix[i-p][j-p]到matrix[i][j]是否全为1
    	matrix[i][j] = p
    else 
    	dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])

从dp[]中获取结果

dp[n-1][m-1]就是最大正方形的边长,求平方就是最后的结果

完整代码

// @lc code=start
/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalSquare = function (matrix) {
  const n = matrix.length;
  if (!n) {
    return 0;
  }
  const m = matrix[0].length;
  const dp = new Array(n).fill(0).map(() => new Array(m));
  dp[0][0] = matrix[0][0];
  for (let i = 1; i < m; i++) {
    dp[0][i] = Math.max(matrix[0][i], dp[0][i - 1]);
  }
  for (let i = 1; i < n; i++) {
    dp[i][0] = Math.max(matrix[i][0], dp[i - 1][0]);
  }
  for (let i = 1; i < n; i++) {
    for (let j = 1; j < m; j++) {
      if (matrix[i][j] === '0') {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      } else {
        const max = Math.max(dp[i - 1][j], dp[i][j - 1]);
        const bigger = max + 1;
        if (bigger > Math.min(i, j) + 1) {
          dp[i][j] = max;
        } else {
          dp[i][j] = helpFn(matrix, i, j, bigger) ? bigger : max;
        }
      }
    }
  }
  console.log(dp);
  const res = dp[n - 1][m - 1];
  console.log(res);
  return Math.pow(res, 2);
};

const helpFn = (matrix, i, j, bigger) => {
  for (let p = i; p > i - bigger; p--) {
    for (let q = j; q > j - bigger; q--) {
      if (matrix[p][q] === '0') {
        return false;
      }
    }
  }
  return true;
};

官方做法比较

本题的官方做法是定义的dp[i][j]表示的是以matrix[i][j]为右下角的最大正方形边长,并且得出的状态转移方程是

dp(i,j)= Math.min(dp(i−1,j),dp(i−1,j−1),dp(i,j−1))+1

最后的结果需要从dp数组中找最大值

总结

最大的感受是,dp数组承载的并不一定是最后的结果,这个思想需要时刻注意一下。