返回

机器人的运动范围深度剖析:DFS、BFS及优化技巧

IOS

问题概述:机器人的运动范围

在“机器人运动范围”问题中,您给定一个机器人位于一个由 0 和 1 构成的 m x n 矩阵中。机器人的目标是从起始位置到达右下角,同时遵守以下规则:

  • 机器人只能向右或向下移动(不能对角线移动)。
  • 如果矩阵中的某个单元格为 0,则机器人可以进入该单元格。
  • 如果矩阵中的某个单元格为 1,则机器人不能进入该单元格。

您的任务是确定机器人能否到达右下角,并返回其可达单元格的数量。

解决方案 1:深度优先搜索 (DFS)

DFS 是一种递归算法,它通过深度探索当前分支来查找解决方案。在机器人运动范围问题中,我们可以使用 DFS 从起始位置开始搜索,并沿着可行的路径探索,直到到达右下角或遍历所有可能的路径。

DFS 的基本实现很简单:

func dfs(_ matrix: [[Int]], _ row: Int, _ col: Int, _ visited: inout Set<(Int, Int)>) -> Int {
    // 边界检查
    guard row >= 0 && row < matrix.count && col >= 0 && col < matrix[0].count && matrix[row][col] == 0 && !(row, col) in visited else {
        return 0
    }

    // 标记单元格已访问
    visited.insert((row, col))

    // 递归探索所有方向
    let count = 1 + // 当前单元格
        dfs(matrix, row + 1, col, &visited) + // 向下移动
        dfs(matrix, row, col + 1, &visited)  // 向右移动

    return count
}

优化 DFS:记忆化搜索

为了优化 DFS,我们可以使用记忆化搜索,也称为动态规划。记忆化搜索通过存储子问题的解来避免重复的计算。在机器人运动范围问题中,我们可以通过存储每个单元格的可达单元格数量来实现记忆化搜索:

var memo = [:]

func dfs(_ matrix: [[Int]], _ row: Int, _ col: Int) -> Int {
    // 边界检查
    guard row >= 0 && row < matrix.count && col >= 0 && col < matrix[0].count && matrix[row][col] == 0 else {
        return 0
    }

    // 检查记忆化表
    if let count = memo[(row, col)] {
        return count
    }

    // 递归探索所有方向
    let count = 1 + // 当前单元格
        dfs(matrix, row + 1, col) + // 向下移动
        dfs(matrix, row, col + 1)  // 向右移动

    // 将解存储到记忆化表中
    memo[(row, col)] = count

    return count
}

通过使用记忆化搜索,我们可以显著减少 DFS 的时间复杂度,使其从 O(2^(m+n)) 优化为 O(mn)。

解决方案 2:广度优先搜索 (BFS)

BFS 是一种基于队列的数据结构的算法,它通过逐层探索所有可能的状态来查找解决方案。在机器人运动范围问题中,我们可以使用 BFS 从起始位置开始搜索,并将所有可行的邻居添加到队列中。然后,我们将从队列中弹出单元格,并探索它们的邻居,以此类推,直到到达右下角或遍历所有可能的路径。

BFS 的基本实现如下:

func bfs(_ matrix: [[Int]], _ start: (Int, Int)) -> Int {
    var queue = [start]
    var visited = Set<String>()
    var count = 0

    while !queue.isEmpty {
        let (row, col) = queue.removeFirst()

        // 边界检查和障碍物检查
        guard row >= 0 && row < matrix.count && col >= 0 && col < matrix[0].count && matrix[row][col] == 0 else {
            continue
        }

        // 标记单元格已访问
        let key = "\(row),\(col)"
        guard !visited.contains(key) else {
            continue
        }
        visited.insert(key)

        // 增加可达单元格数量
        count += 1

        // 添加邻居到队列
        queue.append(contentsOf: [(row + 1, col), (row, col + 1)])
    }

    return count
}

图解 BFS

为了更好地理解 BFS,我们提供了一个图解 BFS 的示例:

输入矩阵:

0 0 1 0
0 0 1 0
0 0 0 0
0 0 0 0

起始位置:(0, 0)

第 1 轮:

访问单元格:`(0, 0)`
添加到队列:`(1, 0)` 和 `(0, 1)`

第 2 轮:

访问单元格:`(1, 0)`
添加到队列:`(2, 0)` 和 `(1, 1)`

访问单元格:`(0, 1)`
添加到队列:`(1, 1)`(已在队列中,忽略)

第 3 轮:

访问单元格:`(2, 0)`
添加到队列:`(3, 0)` 和 `(2, 1)`

访问单元格:`(1, 1)`
添加到队列:`(2, 1)`(已在队列中,忽略)

第 4 轮:

访问单元格:`(3, 0)`
添加到队列:`(4, 0)`(越界,忽略)

访问单元格:`(2, 1)`
添加到队列:`(3, 1)`(越界,忽略)

结束:

遍历所有可能的路径后,可达单元格的数量为 9。

结论

在本文中,我们深入探讨了机器人运动范围问题,并介绍了两种解决它的主要方法:DFS 和 BFS。我们还讨论了 DFS 的优化技术(记忆化搜索)并提供了一个图解 BFS,以帮助您更深入地理解该算法。通过本文,您现在应该装备精良,可以解决此类问题并编写高效且准确的代码。