问题描述
给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。
示例 1:
输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:9
示例 2:
输入:grid = [[1,1,0,0]]
输出:1
提示:
- 1 <= grid.length <= 100
- 1 <= grid[0].length <= 100
- grid[i][j] 为 0 或 1
分析问题
-
先判断这个问题是否适用于动态规划–是否有最优子结构或子问题重叠、
矩阵grid[n][n]的最优解取决矩阵grid[n-1][n-1]–符合最优子结构
-
看属于最优子结构问题还是子问题重叠问题(最优子结构问题肯定是子问题重叠问题)
最优子结构
-
最优子结构的处理方法
- 找到选择–选择整个网格中哪个坐标点作为边界全部由
1
组成的最大 正方形 子网格的右下角。通过该坐标点左边和上面有多少个连续的1,求得其中元素数量。 - 假设一个选择已经是最优解,先不关心这个选择如何得到的,只是假定知道了这个选择。
- 给定选择后,确定这次选择会产生哪些子问题。–该坐标点上面的相邻节点的上边和左边的相邻节点的左边又有多少个连续的1。
- 找到选择–选择整个网格中哪个坐标点作为边界全部由
解决问题步骤
题目要求的是正方形网格,因此我们只需要全是1围成的正方形的边长,就能获取到该网格内元素数量。我们需要对矩阵中每个节点,计算其作为正方形右下角所能构成的最大的正方形的边长–具体方法是记录包括该节点其上方和左方连续1的长度,如果该节点是0则不用记录。之后还需要确定右上角的左边和左下角的上边是否也满足连续的1,才能确定是否可以作为最大边长使用。最后通过不断比较得到最大边长来得到最终结果。以下代码来源于leetcode大佬题解,并非本人写的,觉得这种写法特别简洁,因此借鉴。
class Solution {
public:
int largest1BorderedSquare(vector<vector<int>>& grid) {
int length = grid.size();//矩阵的长
if (length == 0) return 0;
int width = grid[0].size();//矩阵的宽
//用dp[i][j][0]来表示grid[i-1][j-1]的 左边到自己 连续的1的个数
//用dp[i][j][1]来表示grid[i-1][j-1]的 上面到自己 连续的1的个数
//如果grid[i-1][j-1]的值为0,则默认为0,不记录。
//dp大小比grid大小要多一行和一列,方便代码的书写。因此循环从1开始
vector<vector<vector<int>>> dp(length + 1, vector<vector<int>>(width + 1, vector<int>(2, 0)));
int maxLen = 0;
for (int i = 1; i <= length; i++) {
for (int j = 1; j <= width; j++) {
if (grid[i - 1][j - 1] == 1) {
dp[i][j][0] += dp[i][j - 1][0] + 1;
dp[i][j][1] += dp[i - 1][j][1] + 1;
//尝试以第i行第j列(当前点)为右下角构成正方形
//选择的上边和左边较短的最大可能长度,此时只是可能
int len = min(dp[i][j][0], dp[i][j][1]);
while (len > 0) {
//判断这个可能的正方形右上角左侧是否有连续len个1 && 左下角的上方是否有连续len个1
//最终len是确认的正方形的最大边长
if (dp[i - len + 1][j][0] >= len && dp[i][j - len + 1][1] >= len)
break;
len--;
}
maxLen = max(maxLen, len);
}
}
}
return maxLen * maxLen;
}
};