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))

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页