class Solution {
public:
int shortestPath(vector<vector<int>>& grid, int k) {
}
};
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
或 1
grid[0][0] == grid[m-1][n-1] == 0
原站题解
golang 解法, 执行用时: 4 ms, 内存消耗: 2.7 MB, 提交时间: 2023-09-18 15:22:05
// dfs确定状态+疯狂剪枝 func shortestPath(grid [][]int, k int) int { m, n := len(grid), len(grid[0]) if k >= m+n-3 { return m + n - 2 } // 三维记忆数组, [x][y][cnt1] 分别表示坐标和当前墙的数量,存储的值为当前状态下遍历过的最短路径长度 // 当坐标和当前墙的数量相等时,继续带入 dfs, 递归的路径是一样的 // 所以如果某状态下,当前路径长度大于等于 vis 记录的路径长度,继续递归,只会得到更大或相等的结果,所以直接返回 vis := make([][][]int, m) for i := range vis { vis[i] = make([][]int, n) for j := range vis[i] { vis[i][j] = make([]int, k+1) for l := range vis[i][j] { vis[i][j][l] = math.MaxInt } } } var dirs4 = [][2]int{{-1, 0}, {1, 0}, {0, 1}, {0, -1}} ans := math.MaxInt var dfs func(int, int, int, int) dfs = func(x, y, cnt, wallCnt int) { if grid[x][y] == 1 { wallCnt++ // 墙数量+1 } cnt++ // 路径+1 if wallCnt > k { // 多于k个墙 寄 return } if cnt >= ans { // 路径数量大于等于之前的答案,最短,就没必要了 return } if x == m-1 && y == n-1 { // 到达,更新答案 ans = cnt return } if cnt >= vis[x][y][wallCnt] { // 当前路径长度大于等于记忆化的那个,寄 return } vis[x][y][wallCnt] = cnt // 记忆化 for i := 0; i < 4; i++ { // 4偏移量 a, b := x+dirs4[i][0], y+dirs4[i][1] if a >= 0 && a < m && b >= 0 && b < n { dfs(a, b, cnt, wallCnt) } } } dfs(0, 0, -1, 0) if ans == math.MaxInt { // 寄 return -1 } return ans }
python3 解法, 执行用时: 1604 ms, 内存消耗: 23.2 MB, 提交时间: 2023-09-18 15:19:30
class Solution: def shortestPath(self, grid: List[List[int]], k: int) -> int: if not any(grid): return -1 m, n = len(grid), len(grid[0]) k = min(k, m+n-3) q = [(0, 0, k, 0)] visited = {(0, 0, k)} while q: x, y, rest, steps = q.pop(0) if x == m-1 and y == n-1: return steps for nx, ny in [(x+1, y), (x-1, y), (x, y+1), (x, y-1)]: if 0 <= nx < m and 0 <= ny < n: nk = rest-grid[nx][ny] if nk < 0 or (nx, ny, nk) in visited: continue q.append((nx, ny, nk, steps+1)) visited.add((nx, ny, nk)) return -1
java 解法, 执行用时: 3 ms, 内存消耗: 40.3 MB, 提交时间: 2023-09-18 15:18:50
class Solution { // 四个搜寻方向 int[][] directions = new int[][]{{-1,0},{1,0},{0,-1},{0,1}}; public int shortestPath(int[][] grid, int k) { int m = grid.length; int n = grid[0].length; if ( k >= m + n - 3){ return m + n - 2; } boolean[][][] flag = new boolean[m][n][k + 1]; // flag[m][n][k] 表示到达位置 m,n ,且还剩余k个消除障碍物的机会 Queue<int[]> q = new LinkedList<>(); q.add(new int[]{0,0,k}); flag[0][0][k] = true; int step = 0; while ( ! q.isEmpty() ){ step ++; int size = q.size(); while ( size -- > 0){ int[] currentState = q.poll(); int curm = currentState[0]; int curn = currentState[1]; int curk = currentState[2]; for (int[] direction : directions){ // 下一个搜索位置 int nextm = curm + direction[0]; int nextn = curn + direction[1]; // 搜索位置 未越界 if ( ( nextm >= 0 && nextm < m ) && ( nextn >=0 && nextn < n) ){ // 下一个位置nextm,nextn, 不是障碍物 ,且状态 [nextm][nextn][curk] 没被经历过 if ( grid[nextm][nextn] == 0 && !flag[nextm][nextn][curk] ){ // 找到结果 if ( nextm == m - 1 && nextn == n - 1 ){ return step; } // 没找到结果 ,记录当前状态,继续寻找 else{ flag[nextm][nextn][curk] = true; q.add(new int[]{nextm,nextn,curk}); } } // 下一个位置 为障碍物,且当前还有移除障碍物的机会 , // 且 状态[nextm][nextn][curk - 1]没被经历过 else if( grid[nextm][nextn] == 1 && curk > 0 && !flag[nextm][nextn][curk-1]){ flag[nextm][nextn][curk-1] = true; q.add(new int[]{nextm,nextn,curk - 1}); } } } } } return -1; } }
cpp 解法, 执行用时: 64 ms, 内存消耗: 14.9 MB, 提交时间: 2023-09-18 15:18:31
struct Nagato { int x, y; int rest; Nagato(int _x, int _y, int _r): x(_x), y(_y), rest(_r) {} }; class Solution { private: static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public: int shortestPath(vector<vector<int>>& grid, int k) { int m = grid.size(), n = grid[0].size(); if (m == 1 && n == 1) { return 0; } k = min(k, m + n - 3); bool visited[m][n][k + 1]; memset(visited, false, sizeof(visited)); queue<Nagato> q; q.emplace(0, 0, k); visited[0][0][k] = true; for (int step = 1; q.size() > 0; ++step) { int cnt = q.size(); for (int _ = 0; _ < cnt; ++_) { Nagato cur = q.front(); q.pop(); for (int i = 0; i < 4; ++i) { int nx = cur.x + dirs[i][0]; int ny = cur.y + dirs[i][1]; if (nx >= 0 && nx < m && ny >= 0 && ny < n) { if (grid[nx][ny] == 0 && !visited[nx][ny][cur.rest]) { if (nx == m - 1 && ny == n - 1) { return step; } q.emplace(nx, ny, cur.rest); visited[nx][ny][cur.rest] = true; } else if (grid[nx][ny] == 1 && cur.rest > 0 && !visited[nx][ny][cur.rest - 1]) { q.emplace(nx, ny, cur.rest - 1); visited[nx][ny][cur.rest - 1] = true; } } } } } return -1; } };
python3 解法, 执行用时: 576 ms, 内存消耗: 23 MB, 提交时间: 2023-09-18 15:17:54
# bfs class Solution: def shortestPath(self, grid: List[List[int]], k: int) -> int: m, n = len(grid), len(grid[0]) if m == 1 and n == 1: return 0 k = min(k, m + n - 3) visited = set([(0, 0, k)]) q = collections.deque([(0, 0, k)]) step = 0 while len(q) > 0: step += 1 cnt = len(q) for _ in range(cnt): x, y, rest = q.popleft() for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: nx, ny = x + dx, 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 q.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: q.append((nx, ny, rest - 1)) visited.add((nx, ny, rest - 1)) return -1