BFS之网格中的最短路径

本文深入探讨LeetCode1293题目的解决方案,介绍如何在网格中寻找从左上角到右下角的最短路径,同时考虑到消除障碍物的限制。文章详细讲解了广度优先搜索算法的应用,以及如何优化搜索过程,以减少时间和空间复杂度。

LeetCode 1293. 网格中的最短路径

给你一个 m * n 的网格,其中每个单元格不是 0(空)就是 1(障碍物)。每一步,您都可以在空白单元格中上、下、左、右移动。

如果您 最多 可以消除 k 个障碍物,请找出从左上角 (0, 0) 到右下角 (m-1, n-1) 的最短路径,并返回通过该路径所需的步数。如果找不到这样的路径,则返回 -1。

示例 1:

输入: 
grid = 
[[0,0,0],
 [1,1,0],
 [0,0,0],
 [0,1,1],
 [0,0,0]], 
k = 1
输出:6
解释:
不消除任何障碍的最短路径是 10。
消除位置 (3,2) 处的障碍后,最短路径是 6 。
该路径是 (0,0) -> (0,1) -> (0,2) -> (1,2) -> (2,2) -> (3,2) -> (4,2).

示例 2:

输入:
grid = 
[[0,1,1],
 [1,1,1],
 [1,0,0]], 
k = 1
输出:-1
解释:
我们至少需要消除两个障碍才能找到这样的路径。

提示:

grid.length == m
grid[0].length == n
1 <= m, n <= 40
1 <= k <= m*n
grid[i][j] == 0 or 1
grid[0][0] == grid[m-1][n-1] == 0

思路:
由于玩家在最短路中显然不会经过同一位置超过一次,因此最多消除 k 个障碍物等价于最多经过 k 个障碍物。

  • 使用三元组 (x, y, rest) 表示一个搜索状态,其中 (x, y) 表示玩家的位置,rest 表示玩家还可以经过 rest 个障碍物,它的值必须为非负整数。
  • 对于当前的状态 (x, y, rest),它可以向最多四个新状态进行搜索,即将玩家 (x, y) 向四个方向移动一格。假设移动的方向为 (dx, dy),那么玩家的新位置为 (x + dx, y + dy)。
  • 如果该位置为障碍物,那么新的状态为 (x + dx, y + dy, rest - 1),否则新的状态为 (x + dx, y + dy, rest)。
  • 我们从初始状态 (0, 0, k) 开始搜索,当我们第一次到达状态 (m - 1, n - 1, k’),其中 k’ 是任意非负整数时,就得到了从左上角 (0, 0) 到右下角 (m - 1, n - 1) 且最多经过 k 个障碍物的最短路径。

题目中 k 的上限为 m∗nm * nmn,但考虑一条从 (0, 0) 向下走到 (m - 1, 0) 再向右走到 (m - 1, n - 1) 的路径,它经过了 m + n - 1 个位置,其中起点 (0, 0) 和终点 (m - 1, n - 1) 没有障碍物,那么这条路径上最多只会有 m + n - 3 个障碍物
因此我们可以将 k 的值设置为 m + n - 3 与其本身的较小值 min(k, m + n - 3),将广度优先搜索的时间复杂度从 O(MNK)O(MNK)O(MNK) 降低至 O(MN∗min⁡(M+N,K))O(MN * \min(M + N, K))O(MNmin(M+N,K))

class Solution:
    def shortestPath(self, grid: List[List[int]], k: int) -> int:
        m = len(grid)
        n = len(grid[0])
        if m == 1 and n == 1:
            return 0
        # 可以消除的障碍物较多,考虑全为0没有障碍物,那么最短路径是m+n-2
        if m + n - 3 <= k: 
            return m + n - 2
        k = min(m + n - 3, k)
        visited = set([(0, 0 ,k)])
        queue = [(0, 0, k)]
        
        step = 0
        while len(queue) > 0:
            step += 1
            size = len(queue)
            for _ in range(size):
                x, y, rest = queue.pop(0)
                for dx, dy in [(-1, 0), (1, 0), (0, 1), (0, -1)]:
                    nx = x + dx
                    ny = y + dy
                    if 0 <= nx < m and 0 <= ny < n:
                        if grid[nx][ny] == 0 and (nx, ny, rest) not in visited:
                            if nx == m - 1 and ny == n - 1:
                                return step
                            queue.append((nx, ny, rest))
                            visited.add((nx, ny, rest))
                        elif grid[nx][ny] == 1 and rest > 0 and (nx, ny, rest - 1) not in visited:
                            queue.append((nx, ny, rest - 1))
                            visited.add((nx, ny, rest - 1))
        return -1

时间复杂度:O(MN∗min⁡(M+N,K))O(MN * \min(M + N, K))O(MNmin(M+N,K))

空间复杂度:O(MN∗min⁡(M+N,K))O(MN * \min(M + N, K))O(MNmin(M+N,K))

<think>好的,用户需要的是用C++实现BFS算法网格图中计算单源最短路径的方法或代码示例。首先,我需要确认BFS(广度优先搜索)在网格中的适用性。BFS通常用于无权图的最短路径问题,因为逐层扩展可以保证首次到达目标时的路径是最短的。网格中的每个单元格可以视为图中的一个节点,相邻的单元格(上下左右,有时包括对角线)是邻接节点。 接下来,我需要考虑网格的表示方式。通常使用二维数组或矩阵来表示网格,其中每个元素可能包含障碍物信息或其他状态。用户提到的是单源最短路径,所以需要指定起点,可能还需要终点。在BFS中,通常会使用队列来管理待访问的节点,以及一个距离数组或矩阵来记录每个节点到起点的最短距离。 然后,我需要处理障碍物的情况。在网格图中,某些单元格可能不可通过,因此需要在遍历时跳过这些障碍。代码示例中需要包含对障碍物的检查,例如在移动前判断目标单元格是否为可通行区域。 另外,要注意边界条件的处理,比如移动后的坐标是否超出网格的边界。这可以通过在每次移动前检查坐标的有效性来实现。 接下来是代码结构。通常,BFS的实现需要以下步骤: 1. 初始化队列,将起点加入队列,并标记为已访问。 2. 初始化距离数组,起点的距离设为0,其他设为无穷大或-1。 3. 循环处理队列中的每个节点,访问其所有未被访问且可通行的相邻节点,更新距离并将这些节点加入队列。 4. 当队列为空或找到终点时结束循环。 用户可能需要输出最短路径的长度或者路径本身。如果只是长度,距离数组就足够了;如果需要路径,则还需记录前驱节点以便回溯。 现在,参考用户提供的引用内容。引用[2]提到时间复杂度,BFS的时间复杂度是O(V+E),其中V是节点数,E是边数。在网格图中,每个节点最多有4个边(上下左右),所以时间复杂度为O(M*N),M和N是网格的行列数。这需要在回答中提及,以说明算法效率。 此外,引用[2]中还提到了变量、遍历方法等术语,这些可能与代码中的变量命名或方法选择相关,但具体到BFS实现,可能更直接的是队列的使用和遍历方向的处理。 可能的问题点:用户是否允许对角移动?通常网格图的BFS只考虑四方向移动,但需要明确说明。在代码示例中,我应默认四方向,并在注释中提到可扩展为八方向的情况。 代码示例的结构应包括: - 网格的表示,比如二维vector或数组。 - 队列的数据结构,使用STL的queue。 - 方向数组,表示四个移动方向。 - 距离矩阵,记录每个点的最短距离。 - 障碍物检查。 测试用例部分,用户可能需要示例输入和输出,但用户的问题更关注实现方法,因此代码示例应简洁且可运行。 最后,生成相关问题需要考虑用户可能进一步的需求,比如如何处理动态变化的网格BFS与DFS的对比,或者如何优化空间复杂度等。</think>### C++实现BFS计算网格图单源最短路径的方法 #### 算法原理 广度优先搜索(BFS)通过逐层遍历网格中的单元格,确保首次访问到目标点时路径最短。对于$M \times N$的网格,时间复杂度为$O(MN)$,空间复杂度为$O(MN)$[^2]。 #### 代码实现 ```cpp #include <iostream> #include <vector> #include <queue> using namespace std; // 定义方向数组(上、右、下、左) const int dx[] = {-1, 0, 1, 0}; const int dy[] = {0, 1, 0, -1}; vector<vector<int>> bfs_shortest_path(vector<vector<int>>& grid, pair<int, int> start) { int rows = grid.size(); int cols = grid[0].size(); // 初始化距离矩阵,-1表示未访问 vector<vector<int>> dist(rows, vector<int>(cols, -1)); queue<pair<int, int>> q; // 起点处理(假设起点可通行) if (grid[start.first][start.second] == 1) return dist; dist[start.first][start.second] = 0; q.push(start); while (!q.empty()) { auto [x, y] = q.front(); q.pop(); // 遍历四个方向 for (int i = 0; i < 4; ++i) { int nx = x + dx[i]; int ny = y + dy[i]; // 检查边界、可通行性和是否已访问 if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == 0 && dist[nx][ny] == -1) { dist[nx][ny] = dist[x][y] + 1; q.push({nx, ny}); } } } return dist; } ``` #### 使用说明 1. **输入参数**: - `grid`:二维数组,`0`表示可通行区域,`1`表示障碍物 - `start`:起点坐标(行列索引) 2. **输出**: - 距离矩阵,每个单元格的值表示从起点到该位置的最短步数,`-1`表示不可达 #### 示例场景 ```cpp int main() { vector<vector<int>> grid = { {0,1,0,0}, {0,0,0,1}, {1,1,0,0}, {0,0,0,0} }; auto dist = bfs_shortest_path(grid, {3, 0}); // 输出起点(3,0)到(0,3)的最短距离 if (dist[0][3] != -1) { cout << "最短路径长度: " << dist[0][3] << endl; // 输出7 } else { cout << "不可达" << endl; } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值