列表

详情


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
解释:我们至少需要消除两个障碍才能找到这样的路径。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int shortestPath(vector<vector<int>>& grid, int k) { } };

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

上一题