[LeetCode]221. Maximal Square 动态规划解法转移方程

题目描述

LeetCode原题链接:221. Maximal Square

Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

 

Example 1:

Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
Output: 4

Example 2:

Input: matrix = [["0","1"],["1","0"]]
Output: 1

Example 3:

Input: matrix = [["0"]]
Output: 0

Constraints:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 300
  • matrix[i][j] is '0' or '1'.

思路分析

这道题的Related Topic里有Dynamic Programming的标签,那我们就来看看动态规划思路的解法。

1)dp数组构造

我们遍历二维数组一般是按照从上到下、从左到右的顺序进行for循环。当到达一个新的位置(i,j)的时候,数组前 i 行和第 i+1 行前 j 列的位置都是已经遍历过了的。动态规划的核心就是用已知量去推导出未知量,所以(i,j)位置右面和下面的值是怎样无需关心,我们只要去关注上面和左面就可以了。那么,我们可以将当前访问的坐标当作某个可能存在的正方形的右下角,然后来看以该点为右下角的正方形可能的边长。由于只有1才可能组成正方形,当我们遇到0的时候,直接跳过就可以了。dp[m][n]表示一个与matrix矩阵相同大小的动态规划矩阵,dp[i][j]表示以(i,j)为正方形右下角时,存在的合法正方形中面积最大的那个正方形的边长。

当matrix[i][j]=0时,我们直接令dp[i][j]=0(以该位置为右下角的正方形不存在),当matrix[i][j]=1时,我们再进行后续计算。

2)寻找合法正方形

根据正方形的特性可知,可能存在的最大正方形的边长应该是 L = min(i, j)+1 ,我们可以把 L * L 区域当作一个搜寻范围。举个🌰,当遍历到(2,2)时,绿色框框表示以这样一个区域。如下图,这个区域内合法正方形的最大边长分别为3、2、1。

如果当前位置行号和列号不一样,比如下面的矩阵分别遍历到(1,2)、(2,3)、(2,1)的时候,这个绿色框框的范围和范围内合法正方形如下图,这些位置在dp数组中取值都是1。

显然,合法的正方形区域内所有值都应该是1。那么该怎么判断它们都是1呢?我们不可能每次都把搜寻区域从头到尾便利一遍去数1的个数是否是 边长*边长 个,这样做复杂度太高了!回顾dp[i][j]的定义它表示的是一个合法正方形的边长。我们来看看与(i,j)相邻的左上、上面、左面三个位置。下图灰色、蓝色和黄色区域分别表示以(i-1,j-1)、(i-1,j)、(i,j-1)为正方形右下角、最大正方形的面积范围。可以肯定,matrix矩阵中这些区域内的位置取值都是1。

我们把这三个方向的合法正方形区域合并到一起,它们交集范围内所有位置上的值都是1,再加上当前访问的(i,j),正好可以填充成一个新的正方形。新的正方形的边长应该是min(dp[i][j-1], dp[i-1][], dp[i-1][j-1]) + 1。取最小值的目的是为了保证上、左、左上方向都满足值全是1的条件,否则可能会出现在某一方向不相邻位置上出现0的情况。

3)状态转移方程

从上面的分析我们可以看出,dp[i][j]的转移方程是一个关于dp[i-1][j]、dp[i][j-1]和dp[i-1][j-1]的式子。根据定义,后面的三个值有可能是0(表示无法构成合法正方形)或任何>=1的值。一旦这三个值中有一个是0,我们就无法生成一个新的正方形,dp[i][j]只能是一个最小单位即边长为1的正方形(前提是matrix[i][j] == 1);当三个值都不为0时,可以生成一个新的正方形,它的边长是min(dp[i][j-1], dp[i-1][], dp[i-1][j-1]) + 1。两种情况的表达式可以合并。边界情况是第一行和第一列的位置,因为这些位置不具备三个方向上的全部分区,因此最多只能组成边长为1的合法正方形,对于这些位置dp的取值只需要判断当前位置是否是1就可以了。

综上,我们可以得到这道题的转移方程为:

 

 

然后,只需再用一个变量在遍历过程中记录最大的边长就可以得到结果啦!

代码示例(C++)

 1 class Solution {
 2 public:
 3     int maximalSquare(vector<vector<char>>& matrix) {
 4         int m = matrix.size(), n = matrix[0].size(), maxLength = 0;
 5         vector<vector<int>> dp(m, vector<int>(n, 0));
 6         for(int i = 0; i < m; i++) {
 7             for(int j = 0; j < n; j++) {
 8                 // 边界情况
 9                 if(i == 0 || j == 0) {
10                     dp[i][j] = matrix[i][j] - '0';
11                 }
12                 else if(matrix[i][j] == '1'){
13                     // 值为1的时候才有可能构成正方形
14                     dp[i][j] = min(dp[i - 1][j - 1], min(dp[i][j - 1], dp[i - 1][j])) + 1;
15                 }
16                 // 记录当前最大边长
17                 maxLength = max(maxLength, dp[i][j]);
18             }
19         }
20         return maxLength * maxLength;
21     }
22 };
posted @ 2021-08-21 07:56  夭夭夭夭夭桃子  阅读(58)  评论(0编辑  收藏  举报