动态规划—221. 最大正方形
- 前言
- 题目描述
- 基本思路
- 1. 问题定义:
- 2. 理解问题和递推关系:
- 3. 解决方法:
- 3.1 动态规划方法
- 3.2 空间优化的动态规划
- 4. 进一步优化:
- 5. 小总结:
- 代码实现
- Python3代码实现
- Python 代码解释
- C++代码实现
- C++ 代码解释
前言
在二维矩阵中寻找最大正方形的问题是动态规划的一个经典应用。这个问题不仅考察我们对二维数组的操作,还需要我们理解如何通过递推公式优化解法。矩阵中的每个元素都可能成为一个正方形的一部分,而我们要做的就是利用之前的计算结果,在矩阵的每个位置处找到能够扩展出的最大正方形。
本文将介绍如何通过动态规划方法解决这一问题。我们会逐步分析递推关系,并通过 Python 和 C++ 代码示例展示具体的实现,并在最后总结代码的实现细节。
题目描述
基本思路
1. 问题定义:
在一个由 0
和 1
组成的二维矩阵中,找到只包含 1
的最大正方形,并返回其面积。
2. 理解问题和递推关系:
- 该问题的本质是求解在矩阵中可以组成最大正方形的边长,并最终返回其面积。
- 假设 d p [ i ] [ j ] d p[i][j] dp[i][j] 表示以位置 ( i , j ) (i, j) (i,j) 为右下角的最大正方形的边长。为了构成一个更大的正方形,要求该位置的左边、上边和左上角位置的 d p d p dp 值都要有足够的边长。因此,状态转移方程为:
d p [ i ] [ j ] = min ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] ) + 1 d p[i][j]=\min (d p[i-1][j], d p[i][j-1], d p[i-1][j-1])+1 dp[i][j]=min(dp[i−1][j],dp[i][j−1],dp[i−1][j−1])+1
其中, d p [ i − 1 ] [ j ] d p[i-1][j] dp[i−1][j] 表示上边的正方形边长, d p [ i ] [ j − 1 ] d p[i][j-1] dp[i][j−1] 表示左边的正方形边长, d p [ i − 1 ] [ j − 1 ) d p[i-1][j- 1) dp[i−1][j−1)表示左上角的正方形边长。三者的最小值加上当前的 1 1 1 就可以组成一个新的正方形。
- 边界条件:如果当前位置的值是 ’ 0 ′ 0^{\prime} 0′ ,那么 d p [ i ] [ j ] = 0 d p[i][j]=0 dp[i][j]=0 ,因为无法形成任何正方形。
3. 解决方法:
3.1 动态规划方法
- 创建一个二维数组 d p , d p [ i ] [ j ] d p, d p[i][j] dp,dp[i][j] 表示以 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] 为右下角的最大正方形的边长。
- 初始化 d p d p dp 的第一行和第一列,直接等于矩阵的第一行和第一列的值,因为这些位置无法通过左上、上、左来扩展。
- 从第二行、第二列开始,依次计算 d p [ i ] [ j ] d p[i][j] dp[i][j] ,并记录最大正方形的边长。
- 结果为最大边长的平方,即最大正方形的面积。
3.2 空间优化的动态规划
- 由于每次计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 时只依赖于左边、上边和左上角的位置, 因此可以通过一维数组优化空间复杂度。
- 在计算过程中,我们只需要保存当前行和前一行的数据,从而将空间复杂度优化为 O ( n ) O(n) O(n) 。
4. 进一步优化:
- 空间优化:使用一维数组来代替二维 d p d p dp 数组,通过滚动数组的方式减少空间量杂度,从 O ( m ∗ n ) O(m * n) O(m∗n) 降低到 O ( n ) O(n) O(n) 。
5. 小总结:
- 动态规划方法通过逐步扩展矩阵中的 ′ 1 ′ ' 1 ' ′1′,计算出可以形成的最大正方形。该方法在时间昜杂度和空间昜杂度上表现较为优采。
- 通过优化空间复杂度,可以将存储空间减少到 O ( n ) O(n) O(n) ,适用于大规模数据处理。动态规划问题的核心在于找到合理的递推关系,并根据问题特点优化计算过程。
以上就是最大正方形问题的基本思路。
代码实现
Python3代码实现
class Solution:
def maximalSquare(self, matrix: list[list[str]]) -> int:
if not matrix or not matrix[0]:
return 0
rows, cols = len(matrix), len(matrix[0])
dp = [[0] * cols for _ in range(rows)]
max_side = 0
for i in range(rows):
for j in range(cols):
if matrix[i][j] == '1':
if i == 0 or j == 0: # 边界条件,第一行或第一列
dp[i][j] = 1
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
max_side = max(max_side, dp[i][j])
# 最大面积
return max_side * max_side
Python 代码解释
- 初始化:首先初始化 d p dp dp 数组, d p [ i ] [ j ] dp[i][j] dp[i][j] 用于记录以 ( i , j ) (i, j) (i,j) 为右下角的最大正方形的边长。然后,遍历矩阵的每个元素。
- 递推公式:当遇到矩阵元素为
1
时,计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 的值。 d p [ i ] [ j ] dp[i][j] dp[i][j] 取决于左边、上边和左上角三个位置中最小的 d p dp dp 值,然后加 1 1 1。 - 边界条件:如果元素位于第一行或第一列,无法从上方或左方扩展正方形,因此直接将 d p [ i ] [ j ] dp[i][j] dp[i][j] 设为 1 1 1。
- 结果计算:每次计算时更新最大边长 max_side,最后返回最大边长的平方,即最大正方形的面积。
C++代码实现
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[0].empty()) return 0;
int rows = matrix.size();
int cols = matrix[0].size();
vector<vector<int>> dp(rows, vector<int>(cols, 0));
int max_side = 0;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) { // 边界条件,第一行或第一列
dp[i][j] = 1;
} else {
dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
}
max_side = max(max_side, dp[i][j]);
}
}
}
// 最大面积
return max_side * max_side;
}
};
C++ 代码解释
- 初始化:首先创建
dp
数组,dp[i][j]
用于存储以(i, j)
为右下角的最大正方形边长。初始化最大边长 max_side。 - 动态规划递推:遍历矩阵中的每个位置。当
matrix[i][j] == '1'
时,通过递推公式计算dp[i][j]
,更新 max_side。 - 边界条件:对于第一行或第一列,直接将
dp[i][j]
设为1
,因为无法通过左上、上方和左方扩展正方形。 - 结果计算:最终返回最大边长的平方,即最大正方形的面积。