动态规划之最大正方形

56 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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。