There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction.

Given the ball's start position, the destination and the maze, find the shortest distance for the ball to stop at the destination. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the destination (included). If the ball cannot stop at the destination, return -1.

The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes.

Example 1

Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (4, 4) Output: 12
Explanation: One shortest way is : left -> down -> left -> down -> right -> down -> right.
The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12.

Example 2

Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (3, 2) Output: -1
Explanation: There is no way for the ball to stop at the destination.

Note:

  1. There is only one ball and one destination in the maze.
  2. Both the ball and the destination exist on an empty space, and they will not be at the same position initially.
  3. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls.
  4. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.

这道题是之前那道 The Maze 的拓展,那道题只让我们判断能不能在终点位置停下,而这道题让求出到达终点的最少步数。其实本质都是一样的,难点还是在于对于一滚到底的实现方法,唯一不同的是,这里用一个二位数组 dists,其中 dists[i][j] 表示到达 (i,j) 这个位置时需要的最小步数,都初始化为整型最大值,在后在遍历的过程中不断用较小值来更新每个位置的步数值,最后来看终点位置的步数值,如果还是整型最大值的话,说明没法在终点处停下来,返回 -1,否则就返回步数值。注意在压入栈的时候,对x和y进行了判断,只有当其不是终点的时候才压入栈,这样是做了优化,因为如果小球已经滚到终点了,就不要让它再滚了,就不把终点位置压入栈,免得它还滚,参见代码如下:

解法一:

class Solution {
public:
int shortestDistance(vector<vector<int>>& maze, vector<int>& start, vector<int>& destination) {
int m = maze.size(), n = maze[].size();
vector<vector<int>> dists(m, vector<int>(n, INT_MAX));
vector<vector<int>> dirs{{,-},{-,},{,},{,}};
queue<pair<int, int>> q;
q.push({start[], start[]});
dists[start[]][start[]] = ;
while (!q.empty()) {
auto t = q.front(); q.pop();
for (auto d : dirs) {
int x = t.first, y = t.second, dist = dists[t.first][t.second];
while (x >= && x < m && y >= && y < n && maze[x][y] == ) {
x += d[];
y += d[];
++dist;
}
x -= d[];
y -= d[];
--dist;
if (dists[x][y] > dist) {
dists[x][y] = dist;
if (x != destination[] || y != destination[]) q.push({x, y});
}
}
}
int res = dists[destination[]][destination[]];
return (res == INT_MAX) ? - : res;
}
};

上面这种解法的 DFS 形式之前是可以通过 OJ 的,但是现在被卡时间过不了了,代码可以参见评论区第二十三楼。我们还可以使用迪杰斯特拉算法 Dijkstra Algorithm 来做,LeetCode 中能使用到此类高级算法的时候并不多,Network Delay Time 就是一次。该算法是主要是在有向权重图中计算单源最短路径,即单个点到任意点到最短路径。因为这里起点只有一个,所以适用,然后迷宫中的每个格子都可以看作是图中的一个结点,权重可以都看成是1,那么就可以当作是有向权重图来处理。Dijkstra 算法的核心是松弛操作 Relaxtion,当有对边 (u, v) 是结点u到结点v,如果 dist(v) > dist(u) + w(u, v),那么 dist(v) 就可以被更新,这是所有这些的算法的核心操作。Dijkstra 算法是以起点为中心,向外层层扩展,直到扩展到终点为止。那么看到这里,你可能会有个疑问,到底 Dijkstra 算法和 BFS 算法究竟有啥区别。这是个好问题,二者在求最短路径的时候很相似,但却还是有些区别的。首先 Dijkstra 算法是求单源点的最短路径,图需要有权重,而且权重值不能为负,这道题中两点之间的距离可以看作权重,而且不会为负,满足要求。而 BFS 算法是从某点出发按广度优先原则依次访问连通的结点,图可以无权重。另外一点区别就是,BFS 算法是将未访问的邻居压入队列,然后再将未访问邻居的未访问过的邻居入队列再依次访问,而 Dijkstra 算法是在剩余的为访问过的结点中找出权重最小的并访问,这就是为什么要用一个优先队列(最小堆)来代替普通的 queue,这样就能尽量先更新离起点近的位置的 dp 值,优先队列里同时也存了该点到起点的距离,这个距离不一定是最短距离,可能还能松弛。但是如果其 dp 值已经小于优先队列中保存的距离,那么就不必更新其周围位置的距离了,因为优先队列中保存的这个距离值不是最短的,使用它来更新周围的 dp 值没有意义。这相当于利用了松弛操作来进行剪枝,大大提高了运算效率,之后就是类似于之前的 BFS 的操作了,遍历其周围的四个位置,尝试去更新其 dp 值。最后还是跟之前一样,如果遍历到了终点,就不要再排入队列了,因为已经得到需要的结果了,参见代码如下:

解法二:

class Solution {
public:
int shortestDistance(vector<vector<int>>& maze, vector<int>& start, vector<int>& destination) {
int m = maze.size(), n = maze[].size();
vector<vector<int>> dists(m, vector<int>(n, INT_MAX));
vector<vector<int>> dirs{{,-},{-,},{,},{,}};
auto cmp = [](vector<int>& a, vector<int>& b) {
return a[] > b[];
};
priority_queue<vector<int>, vector<vector<int>>, decltype(cmp) > pq(cmp);
pq.push({start[], start[], });
dists[start[]][start[]] = ;
while (!pq.empty()) {
auto t = pq.top(); pq.pop();for (auto dir : dirs) {
int x = t[], y = t[], dist = dists[t[]][t[]];
while (x >= && x < m && y >= && y < n && maze[x][y] == ) {
x += dir[];
y += dir[];
++dist;
}
x -= dir[];
y -= dir[];
--dist;
if (dists[x][y] > dist) {
dists[x][y] = dist;
if (x != destination[] || y != destination[]) pq.push({x, y, dist});
}
}
}
int res = dists[destination[]][destination[]];
return (res == INT_MAX) ? - : res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/505

类似题目:

The Maze

The Maze III

参考资料:

https://leetcode.com/problems/the-maze-ii/

https://leetcode.com/problems/the-maze-ii/discuss/98401/java-accepted-dfs

https://leetcode.com/problems/the-maze-ii/discuss/98456/simple-c-bfs-solution

https://leetcode.com/problems/the-maze-ii/discuss/98427/2-Solutions:-BFS-and-Dijkstra's.-Detailed-explanation..-But-why-is-BFS-faster

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 505. The Maze II 迷宫之二的更多相关文章

  1. [LeetCode] 505. The Maze II 迷宫 II

    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...

  2. [LeetCode] The Maze II 迷宫之二

    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...

  3. LeetCode 505. The Maze II

    原题链接在这里:https://leetcode.com/problems/the-maze-ii/ 题目: There is a ball in a maze with empty spaces a ...

  4. [LeetCode] 499. The Maze III 迷宫 III

    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...

  5. 【LeetCode】505. The Maze II 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS 日期 题目地址:https://leetcod ...

  6. 505. The Maze II

    原题链接:https://leetcode.com/articles/the-maze-ii/ 我的思路 在做完了第一道迷宫问题 http://www.cnblogs.com/optor/p/8533 ...

  7. [LeetCode] 253. Meeting Rooms II 会议室之二

    Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si ...

  8. [LeetCode] 213. House Robber II 打家劫舍之二

    You are a professional robber planning to rob houses along a street. Each house has a certain amount ...

  9. [LC] 505. The Maze II

    There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...

随机推荐

  1. 大话设计模式Python实现-模板方法模式

    模板方法模式(Template Method Pattern):定义一个操作中的算法骨架,将一些步骤延迟至子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 下面是一个模 ...

  2. 行为驱动:Cucumber + Java - 实现数据的参数化

    1.什么是参数化 实际设计测试用例过程中,我们经常会用等价类.边界值这样的方法,针对一个功能进行测试数据上的测试,比如一个输入框,正向数据.逆向数据,非法输入等等 2.Cucumber的数据驱动 同上 ...

  3. pymysql 读取大数据内存卡死的解决方案

    背景:目前表中只有5G(后期持续增长),但是其中一个字段(以下称为detail字段)存了2M(不一定2M,部分为0,平均下来就是2M),字段中存的是一个数组,数组中存N个json数据.这个字段如下: ...

  4. Vue devtool插件安装后无法使用,提示“vue.js not detected”的解决方法

    vue devtool下载 极简插件  github vue devtool安装 点击谷歌浏览器箭头所指图标-更多工具-扩展程序   ①:直接将后缀为crx的安装包拖进下图区域即可自动安装     ② ...

  5. python I/O多路复用 使用http完成http请求

    1. 使用类实现比较方便我们使用里面的参数 2. 我们使用selector,不适用select from selectors import DefaultSelector 3. I/O多路复用是指使用 ...

  6. 在.net中读写config文件的各种方法【转】

    今天谈谈在.net中读写config文件的各种方法. 在这篇博客中,我将介绍各种配置文件的读写操作. 由于内容较为直观,因此没有过多的空道理,只有实实在在的演示代码, 目的只为了再现实战开发中的各种场 ...

  7. Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(四)

    经过上一篇,里面有测试代码,循环60万次,耗时14秒.本次我们增加缓存来优化它. DbContextExtensions.cs using System; using System.Collectio ...

  8. virsh console配置

    If you're trying to get to the console, you can either use virt-viewer for the graphical console or ...

  9. 使用 Logstash 和 JDBC 确保 Elasticsearch 与关系型数据库保持同步

    为了充分利用 Elasticsearch 提供的强大搜索功能,很多公司都会在既有关系型数据库的基础上再部署Elasticsearch.在这种情况下,很可能需要确保 Elasticsearch 与所关联 ...

  10. laravel 163发送邮件

    配置163邮箱账户 首先需要有163邮箱,这里在163邮箱必须在设置里面开启SMTP服务,并设置密码 修改laravel根目录下的.env文件, 设置邮箱相关内容: MAIL_DRIVER=smtp ...