题目地址:
https://leetcode.com/problems/the-maze-iii/
给定一个二维
0
−
1
0-1
0−1矩阵,
0
0
0代表空地,
1
1
1代表障碍物,再给定一个坐标代表一个洞。在某个空地上有个小球,它可以沿着四个方向滑动,每次滑动时只有遇到边界或者障碍物或者掉进洞里才会停下来(掉进洞只需经过洞就可以了)。问该小球是否可能滑到洞里,若可能,则返回最短路径的路径走法(
u
u
u代表向上,
d
d
d代表向下,
l
l
l代表向左,
r
r
r代表向右),若同时存在两种以上最短路径的走法,返回字典序最小的那条路径;若不可能,返回"impossible"
。
题目类似于无向带权图的单源最短路问题,所以可以用Dijkstra算法。先将源点加入一个最小堆(按照与源点的路径距离排序,距离小者优先;若距离相等,则按照路径的字典序排序,字典序先者优先),然后将源点出堆,判断其是否已经到了终点,如果到了则直接返回路径长度(Dijkstra算法可以保证出堆的那个点的路径长度就是源点到该点最短路径长度),否则将这个点能到达的点加入最小堆(如果某次从堆中出来的点是 A A A,当算能到达的点的时候,将这些点的路径长度设为这个点与 A A A的距离加上源点与 A A A的距离,即两段距离的和。另外注意,由于算法保证了出堆的点对应的距离就是最短路的距离,所以要标记其为已访问过,以后再遇到这个点的时候就直接跳过),加入堆后再重复上述过程即可。代码如下:
class Solution {
public:
struct Node {
int x, y;
int dist;
string path;
};
string findShortestWay(vector<vector<int>> &g, vector<int> &beg,
vector<int> &end) {
int m = g.size(), n = g[0].size();
int ex = end[0], ey = end[1];
static auto cmp = [](const Node &n1, const Node &n2) {
return n1.dist != n2.dist ? n1.dist > n2.dist : n1.path > n2.path;
};
static vector<int> d[4] = {{1, 0}, {0, -1}, {0, 1}, {-1, 0}};
static char dch[] = "dlru";
vector<vector<bool>> vis(m, vector<bool>(n));
priority_queue<Node, vector<Node>, decltype(cmp)> heap(cmp);
heap.push({beg[0], beg[1], 0, ""});
int dist[m][n];
memset(dist, 0x3f, sizeof dist);
while (heap.size()) {
auto t = heap.top();
heap.pop();
int x = t.x, y = t.y, dis = t.dist;
if (vis[x][y]) continue;
if (x == ex && y == ey) return t.path;
vis[x][y] = true;
dist[x][y] = dis;
for (int k = 0; k < 4; k++) {
int dx = d[k][0], dy = d[k][1];
x = t.x, y = t.y;
int step = 0;
while (0 <= x + dx && x + dx < m && 0 <= y + dy && y + dy < n &&
g[x + dx][y + dy] != 1) {
x += dx, y += dy;
step++;
if (x == ex && y == ey) break;
}
if (!vis[x][y] && dist[x][y] >= dis + step) {
dist[x][y] = dis + step;
heap.push({x, y, dist[x][y], t.path + dch[k]});
}
}
}
return "impossible";
}
};
时间复杂度 O ( m n log ( m n ) ) O(mn\log (mn)) O(mnlog(mn)),空间 O ( m n ) O(mn) O(mn)。