BFS之网格中的最短路径

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 ∗ n m * n mn,但考虑一条从 (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 ( M N K ) O(MNK) O(MNK) 降低至 O ( M N ∗ 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 ( M N ∗ min ⁡ ( M + N , K ) ) O(MN * \min(M + N, K)) O(MNmin(M+N,K))

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
求解网格任意两点间最短路径可以使用广度优先搜索算法BFS)。具体步骤如下: 1. 定义一个队列,将起点加入队列; 2. 定义一个二维数组 $visited$ 来记录每个格子是否已经被访问过; 3. 定义一个二维数组 $distance$ 来记录每个格子到起点的距离,初始化为 $0$; 4. 定义一个二维数组 $parent$ 来记录每个格子的前驱节点; 5. 循环队列,直到队列为空: - 取出队列的一个点,记为 $cur$; - 如果 $cur$ 是终点,则停止搜索,输出路径; - 遍历 $cur$ 的四个相邻格子 $next$,如果 $next$ 没有被访问过,则将其加入队列,并更新 $visited$、$distance$ 和 $parent$ 数组; 6. 如果队列为空,但是还没有找到终点,则说明起点与终点不连通。 以下是 PHP 代码实现: ```php function shortestPath($grid, $start, $end) { $m = count($grid); $n = count($grid[0]); // 定义队列和状态数组 $queue = []; $visited = []; $distance = []; $parent = []; // 初始化状态数组 for ($i = 0; $i < $m; $i++) { $visited[$i] = array_fill(0, $n, false); $distance[$i] = array_fill(0, $n, INF); $parent[$i] = array_fill(0, $n, null); } // 加入起点 $queue[] = $start; $visited[$start[0]][$start[1]] = true; $distance[$start[0]][$start[1]] = 0; // BFS while (!empty($queue)) { // 取出队首元素 $cur = array_shift($queue); // 检查是否到达终点 if ($cur[0] == $end[0] && $cur[1] == $end[1]) { break; } // 遍历相邻格子 $dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]; foreach ($dirs as $dir) { $next = [$cur[0] + $dir[0], $cur[1] + $dir[1]]; // 如果相邻格子合法且未访问过 if ($next[0] >= 0 && $next[0] < $m && $next[1] >= 0 && $next[1] < $n && !$visited[$next[0]][$next[1]]) { // 加入队列 $queue[] = $next; $visited[$next[0]][$next[1]] = true; $distance[$next[0]][$next[1]] = $distance[$cur[0]][$cur[1]] + 1; $parent[$next[0]][$next[1]] = $cur; } } } // 构造路径 $path = []; $cur = $end; while ($cur != null) { $path[] = $cur; $cur = $parent[$cur[0]][$cur[1]]; } $path = array_reverse($path); return $path; } ``` 其,$grid$ 是一个 $m \times n$ 的二维数组,表示网格地图;$start$ 和 $end$ 分别是起点和终点的坐标。函数返回一个二维数组,表示从起点到终点的最短路径

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值