[LeetCode] 505. The Maze II 迷宮之二


 

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[0].size();
        vector<vector<int>> dists(m, vector<int>(n, INT_MAX));
        vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
        queue<pair<int, int>> q;
        q.push({start[0], start[1]});
        dists[start[0]][start[1]] = 0;
        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 >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0) {
                    x += d[0];
                    y += d[1];
                    ++dist;
                }
                x -= d[0];
                y -= d[1];
                --dist;
                if (dists[x][y] > dist) {
                    dists[x][y] = dist;
                    if (x != destination[0] || y != destination[1]) q.push({x, y});
                }
            }
        }
        int res = dists[destination[0]][destination[1]];
        return (res == INT_MAX) ? -1 : 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[0].size();
        vector<vector<int>> dists(m, vector<int>(n, INT_MAX));
        vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
        auto cmp = [](vector<int>& a, vector<int>& b) {
            return a[2] > b[2];
        };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp) > pq(cmp);
        pq.push({start[0], start[1], 0});
        dists[start[0]][start[1]] = 0;
        while (!pq.empty()) {
            auto t = pq.top(); pq.pop();for (auto dir : dirs) {
                int x = t[0], y = t[1], dist = dists[t[0]][t[1]];
                while (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0) {
                    x += dir[0];
                    y += dir[1];
                    ++dist;
                }
                x -= dir[0];
                y -= dir[1];
                --dist;
                if (dists[x][y] > dist) {
                    dists[x][y] = dist;
                    if (x != destination[0] || y != destination[1]) pq.push({x, y, dist});
                }
            }
        }
        int res = dists[destination[0]][destination[1]];
        return (res == INT_MAX) ? -1 : 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 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM