持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情
动态规划(Dynamic Programming)是一种分阶段求解决策问题的数学思想,它通过把原问题分解为简单的子问题来解决复杂问题。
最大正方形
在一个由 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的位置就作为正方形左上角的起始位置,然后根据这个位置所在的行和列计算出可能的最大正方形边长范围,在边长范围里面,每次在正方形下方新增一行以及在右方新增一列,判断新增的行和列是否满足所有元素都是 1,并记录正方形的最大边长,最后计算只包含1的最大正方形面积。
fun maximalSquare(matrix: Array<CharArray>): Int {
if (matrix.isEmpty() || matrix[0].isEmpty()) {
return 0
}
var max = 0
val m = matrix.size
val n = matrix[0].size
for (i in 0 until m) {
for (j in 0 until n) {
if (matrix[i][j] == '1') {
max = max.coerceAtLeast(1)
val curMaxSide = (m - i).coerceAtMost(n - j)
for (k in 1 until curMaxSide) {
var flag = true
if (matrix[i + k][j + k] == '0') {
break
}
for (p in 0 until k) {
if (matrix[i + p][j + k] == '0' || matrix[i + k][j + p] == '0') {
flag = false
break
}
}
max = if (flag) {
max.coerceAtLeast(k + 1)
} else {
break
}
}
}
}
}
return max * max
}
使用暴力方法的时间复杂度很高,可以使用动态规划降低时间复杂度。
动态规划
用 dp[i,j] 表示以 [i,j] 为右下角,且只包含 1 的正方形的边长最大值。首先,计算出所有 dp[i,j] 的值,然后选出其中的最大值即为矩阵中只包含 1 的正方形的边长最大值,其平方即为最大正方形的面积。
fun maximalSquare(matrix: Array<CharArray>): Int {
if (matrix.isEmpty() || matrix[0].isEmpty()) {
return 0
}
var max = 0
val m = matrix.size
val n = matrix[0].size
val dp = Array(m) { IntArray(n) }
for (i in 0 until m) {
for (j in 0 until n) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) {
dp[i][j] = 1
} else {
dp[i][j] = dp[i][j - 1].coerceAtMost(dp[i - 1][j - 1].coerceAtMost(dp[i - 1][j])) + 1
}
max = max.coerceAtLeast(dp[i][j])
}
}
}
return max * max
}
复杂度分析
-
时间复杂度:O(mn)。需要遍历原始矩阵中的每个元素计算dp的值。
-
空间复杂度:O(mn)。创建了一个和原始矩阵大小相同的矩阵 dp。