1. 程式人生 > >LC 499. The Maze III 【lock,hard】

LC 499. The Maze III 【lock,hard】

There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up(u), down (d), left (l) or right (r), but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. There is also a hole in this maze. The ball will drop into the hole if it rolls on to the hole.

Given the ball position, the hole position and the maze, find out how the ball could drop into the hole by moving the shortest distance. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the hole (included). Output the moving directions by using 'u', 'd', 'l' and 'r'. Since there could be several different shortest ways, you should output the lexicographically smallest way. If the ball cannot reach the hole, output "impossible".

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 ball and the hole coordinates are represented by row and column indexes.

 

Example 1:

Input 1: a maze represented by a 2D array

0 0 0 0 0
1 1 0 0 1
0 0 0 0 0
0 1 0 0 1
0 1 0 0 0

Input 2: ball coordinate (rowBall, colBall) = (4, 3)
Input 3: hole coordinate (rowHole, colHole) = (0, 1)

Output: "lul"

Explanation: There are two shortest ways for the ball to drop into the hole.
The first way is left -> up -> left, represented by "lul".
The second way is up -> left, represented by 'ul'.
Both ways have shortest distance 6, but the first way is lexicographically smaller because 'l' < 'u'. So the output is "lul".

Example 2:

Input 1: a maze represented by a 2D array

0 0 0 0 0
1 1 0 0 1
0 0 0 0 0
0 1 0 0 1
0 1 0 0 0

Input 2: ball coordinate (rowBall, colBall) = (4, 3)
Input 3: hole coordinate (rowHole, colHole) = (3, 0)

Output: "impossible"

Explanation: The ball cannot reach the hole.

 

Note:

  1. There is only one ball and one hole in the maze.
  2. Both the ball and hole 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 the width and the height of the maze won't exceed 30.

這道題DEBUG,DE了半天,用了樸素的DFS,但還是超時在大case上了。果然已經超出了我的能力範圍。

class Solution {
private:
    int arr[4][2] = { { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
    vector<string> directions = { "r","l","d","u" };
public:
    string findShortestWay(vector<vector<int>>& maze, vector<int>& ball, vector<int>& hole) {
        vector<string> ret;
        set<int> visited;
        helper(maze, ball[0], ball[1], hole, ret, visited, "");
        sort(ret.begin(), ret.end());
        string tmpstr = "";
        if (ret.empty()) return "impossible";
        //for(auto str : ret)cout << str << endl;
        for (int i = 0; i < ret[0].size(); i++) {
            if (tmpstr.empty()) {
                tmpstr += ret[0][i];
            }
            else {
                if (tmpstr.back() != ret[0][i]) {
                    tmpstr += ret[0][i];
                }
            }
        }
        return tmpstr;
    }
    void helper(vector<vector<int>>& maze, int x, int y, vector<int>& hole, vector<string>& ret, set<int>& visited, string path) {
        int m = maze.size();
        int n = maze[0].size();
        if (visited.count(x * n + y)) return;
        if (!ret.empty() && path.size() > ret[0].size()) return;
        visited.insert(x*n + y);
        for (int i = 0; i<4; i++) {
            int dx = arr[i][0];
            int dy = arr[i][1];
            int tmpx, tmpy;
            tmpx = x + dx;
            tmpy = y + dy;
            string tmppath = path;
            while (tmpx < m && tmpx >= 0 && tmpy<n && tmpy >= 0 && maze[tmpx][tmpy] != 1) {
                    tmppath += directions[i];
                if (tmpx == hole[0] && tmpy == hole[1]) {
                    // cout << tmppath << endl;
                     if(ret.empty() || tmppath.size() < ret[0].size()){
                       ret.clear();
                       ret.push_back(tmppath);
                     }
                     else if(tmppath.size() == ret[0].size()){
                       ret.push_back(tmppath);
                     }
                    //ret.push_back(tmppath);
                    //visited.erase(x*n + y);
                    //return;
                }
                tmpx += dx; tmpy += dy;
            }
            tmpx -= dx; tmpy -= dy;
            if (tmpx == x && tmpy == y) continue;
            //cout << "tmp location " << tmpx << " and " << tmpy << endl;
            //cout << path << endl;
            helper(maze, tmpx, tmpy, hole, ret, visited, tmppath);
        }
        visited.erase(x*n + y);
    }
};

 

下面是我的解法的改進版,調了兩個小時...

核心在於初始化一個最大值陣列,記錄每一個小球能滯留的點,如果這個點的值被更新且小於當前的cost直接返回,因為之前經過這個點的時候,就已經是比現在小的cost,再搜尋下去沒有意義。

然後在找到終點之後,更新結果,AC的結果依舊慘不忍睹。

Runtime: 160 ms, faster than 8.20% of C++ online submissions for The Maze III.

class Solution {
private:
    int arr[4][2] = { { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
    vector<string> directions = { "r","l","d","u" };
public:
    string findShortestWay(vector<vector<int>>& maze, vector<int>& ball, vector<int>& hole) {
        string ret = "";
        vector<vector<int>> visited(maze.size(), vector<int>(maze[0].size(), INT_MAX));
        helper(maze, ball[0], ball[1], hole, ret, visited, "", 0);
        if (ret.empty()) return "impossible";
        return ret;
    }
    void helper(vector<vector<int>>& maze, int x, int y, vector<int>& hole, string& ret, vector<vector<int>>& visited, string path, int cost) {
        int m = maze.size();
        int n = maze[0].size();
        if (visited[x][y] >= cost) {
            visited[x][y] = cost;
        }
        else {
            return;
        }
        for (int i = 0; i<4; i++) {
            int dx = arr[i][0];
            int dy = arr[i][1];
            int tmpx, tmpy, tmpcost = cost;
            bool check = false, found = false, inloop = false;
            tmpx = x;
            tmpy = y;
            string tmppath = path;
            while (tmpx < m && tmpx >= 0 && tmpy<n && tmpy >= 0 && maze[tmpx][tmpy] != 1) {
                if (!check) {
                    tmppath += directions[i];
                    check = true;
                }
                if (tmpx == hole[0] && tmpy == hole[1]) {
                    if (visited[tmpx][tmpy] > tmpcost) {
                        visited[tmpx][tmpy] = tmpcost;
                        ret = tmppath;
                    }
                    else if(visited[tmpx][tmpy] == tmpcost && ret > tmppath){
                        ret = tmppath;
                    }
                    found = true;
                }
                tmpx += dx; tmpy += dy;
                tmpcost++;
                inloop = true;
            }
            if (inloop) {
                tmpx -= dx;
                tmpy -= dy;
                tmpcost--;
            }
            if (tmpx == x && tmpy == y) continue;
            //if (found) continue;
            //cout << "tmp location " << tmpx << " and " << tmpy << endl;
            //cout << path << endl;
            helper(maze, tmpx, tmpy, hole, ret, visited, tmppath, tmpcost);
        }
    }
};

 

下面是網上的解法,用的是BFD。要求圖上某一個點到已知點距離最近的方法就是BFS。但我覺得能存在更好的辦法(Dijkstra)。

Runtime: 4 ms, faster than 100.00% of C++ online submissions for The Maze III.

#include "header.h"
class Solution {
public:
    vector<vector<int>> dir = { { 1,0, 0 },{ 0,1,1 },{ -1,0,2 },{ 0,-1,3 } };
    vector<string> ds = { "d", "r", "u", "l" };
    //d, r, u, l
    string findShortestWay(vector<vector<int>>& maze, vector<int>& ball, vector<int>& hole) {
        //2018-12-18 解決了maze I and maze II,我覺得本質上還是BFS的問題,這個時候的destination只是說變成了如果掉進了hole,那麼就不在運動了這樣
        //哈哈哈哈哈哈一次AC!贊!
        if (maze.empty()) return "";
        int m = maze.size();
        int n = maze[0].size();
        vector<vector<pair<int, string> >> check(m, vector<pair<int, string> >(n, { INT_MAX, "" }));
        check[ball[0]][ball[1]] = { 0, "" };
        queue<pair<int, int>> q;
        q.push({ ball[0], ball[1] });
        while (!q.empty()) {
            int len = q.size();
            while (len) {
                auto p = q.front(); q.pop(); len--;
                for (auto d : dir) {
                    int steps = 0;
                    int i = p.first; int j = p.second;
                    while (i + d[0] >= 0 && i + d[0] < m && j + d[1] >= 0 && j + d[1] < n && !maze[i + d[0]][j + d[1]]) {
                        i = i + d[0]; j = j + d[1]; steps++;
                        if (i == hole[0] && j == hole[1]) { break; }
                    }
                    if (check[i][j].first > check[p.first][p.second].first + steps) {
                        check[i][j].first = check[p.first][p.second].first + steps;
                        check[i][j].second = check[p.first][p.second].second + ds[d[2]];
                        q.push({ i, j });
                    }
                    else if (check[i][j].first == check[p.first][p.second].first + steps) {
                        if (check[i][j].second > check[p.first][p.second].second + ds[d[2]]) {
                            q.push({ i, j });
                            check[i][j].second = check[p.first][p.second].second + ds[d[2]];
                        }
                    }
                }
            }
        }
        return check[hole[0]][hole[1]].first == INT_MAX ? "impossible" : check[hole[0]][hole[1]].second;
    }
};