LeetCode:1293 网格中的最短路径 三维BFS

题目描述

给你一个 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-path-in-a-grid-with-obstacles-elimination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

如果是一般网格直接bfs秒解,但是关键是这个题,它允许你消除部分障碍,这就蛋疼了,但是通过分析我们可以发现:

可以消除k个障碍,等价于可以 穿过 k个障碍

对于一个格子,我们有两个选择:走 或者 不走

  • 如果格子不是障碍,那走
  • 如果格子是障碍,但是我们允许穿行的次数大于0,那么走
  • 其他情况不走

那么我们的状态变为:

在坐标x,y上,还能翻过 r 个障碍

那么同样的,我们的访问控制数组vis也要变成三维的,原因如下:假设我们有两条到达p点的路径

  1. 翻过 5 个障碍到达 p 点
  2. 翻过 3 个障碍到达 p 点

是不同的情况,无法取舍二者,如果单单凭借 二维 vis[p点] = 1,就舍弃掉某一路径,会丢失最优解,甚至是丢失解

因为无论那种情况,都有可能对应着最优解,那么我们需要额外的空间记录这种状态

vis[x][y][r] 表示在【还可以翻过 r 个障碍】的情况下
坐标为 x,y 的点,是否被访问过

这样确保翻越不同个数的障碍到达同一点的情况不应该被舍弃

于是可以很快的写出bfs的代码
注意入队时标记访问,而不是出队时,不然会丢失解,我暂时没想明白为啥

class Solution {
public:
    // x,y坐标,r还可以翻过多少障碍
    typedef struct p
    {
        int x; int y; int r;
        p(int X, int Y, int R):x(X),y(Y),r(R){}
    }p;
    int shortestPath(vector<vector<int>>& grid, int k)
    {
        int ans=0, m=grid.size(), n=grid[0].size();
        vector<int> biasx{-1,1,0,0}, biasy{0,0,-1,1};
        // 三维vis数组
        bool vis[m][n][k + 1]; memset(vis, false, sizeof(vis));
        queue<p> q; q.push(p(0, 0, k));
        while(!q.empty())
        {
            int qs = q.size();
            for(int i=0; i<qs; i++)
            {
                p tp=q.front(); q.pop();
                if(tp.x==m-1 && tp.y==n-1) return ans;
                for(int i=0; i<4; i++)
                {
                    int X=tp.x+biasx[i], Y=tp.y+biasy[i];
                    if(0<=X && X<m && 0<=Y && Y<n) 
                    {
                        // 如果为0直接进
                        if(grid[X][Y]==0 && !vis[X][Y][tp.r]) 
                        {
                            q.push(p(X, Y, tp.r));
                            vis[X][Y][tp.r] = true;
                        }
                        // 如果为1但是还可以翻,那就翻过去
                        else if(grid[X][Y]==1 && tp.r>0 && !vis[X][Y][tp.r-1]) 
                        {
                            q.push(p(X, Y, tp.r-1));
                            vis[X][Y][tp.r-1] = true;
                        }
                    }
                }
            }
            ans++;
        }
        return -1;
    }
};
发布了199 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104752314