题目
//由空地和墙组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。
//
// 给定球的起始位置,目的地和迷宫,判断球能否在目的地停下。
//
// 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。
//
//
//
// 示例 1:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
//输入 3: 目的地坐标 (rowDest, colDest) = (4, 4)
//
//输出: true
//
//解析: 一个可能的路径是 : 左 -> 下 -> 左 -> 下 -> 右 -> 下 -> 右。
//
//
//
// 示例 2:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
//输入 3: 目的地坐标 (rowDest, colDest) = (3, 2)
//
//输出: false
//
//解析: 没有能够使球停在目的地的路径。
//
//
//
//
//
// 注意:
//
//
// 迷宫中只有一个球和一个目的地。
// 球和目的地都在空地上,且初始时它们不在同一位置。
// 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。
// 迷宫至少包括2块空地,行数和列数均不超过100。
//
// Related Topics 深度优先搜索 广度优先搜索
// 81 0
题解
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
// 迷宫的长宽
int row = maze.length;
int col = maze[0].length;
// 运动方向: 上, 右, 下, 左
int[] dx = new int[]{-1, 0, 1, 0};
int[] dy = new int[]{0, 1, 0, -1};
// 存放转向的坐标
int[][] visited = new int[row][col];
Queue<int[]> queue = new LinkedList<>();
// 将起始坐标标记为已访问, 并将其存放到队列中
queue.add(start);
visited[start[0]][start[1]] = 1;
// 如果队列不为空
while (!queue.isEmpty()) {
// 读取队首元素
int[] cur = queue.poll();
// 判断队首元素是否等于终点坐标, 若等于, 则直接返回true
if (Arrays.equals(cur, destination)) {
return true;
}
// 从当前坐标向四个方向移动.
for (int d = 0; d < 4; d++) {
int x = cur[0];
int y = cur[1];
// 如果没有碰到边界, 且没有遇到墙, 则一直运行
while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
x += dx[d];
y += dy[d];
}
// 到这里已经碰到了墙壁, 所以需要回退一步.
x -= dx[d];
y -= dy[d];
// 因为只能在遇到墙的时候才能更改方向, 所以在遍历时只需要保存转变方向时的坐标, 而不能保存所有经过的点.
// 判断当前坐标是否已被访问,
// 如果没访问则添加的到队列, 并标记为已访问
if (visited[x][y] == 0) {
queue.add(new int[]{x, y});
visited[x][y] = 1;
}
}
}
return false;
}
题目
//由空地和墙组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。
//
// 给定球的起始位置,目的地和迷宫,找出让球停在目的地的最短距离。距离的定义是球从起始位置(不包括)到目的地(包括)经过的空地个数。如果球无法停在目的地,返回
// -1。
//
// 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。
//
//
//
// 示例 1:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
//输入 3: 目的地坐标 (rowDest, colDest) = (4, 4)
//
//输出: 12
//
//解析: 一条最短路径 : left -> down -> left -> down -> right -> down -> right。
// 总距离为 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12。
//
//
//
// 示例 2:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
//输入 3: 目的地坐标 (rowDest, colDest) = (3, 2)
//
//输出: -1
//
//解析: 没有能够使球停在目的地的路径。
//
//
//
//
//
// 注意:
//
//
// 迷宫中只有一个球和一个目的地。
// 球和目的地都在空地上,且初始时它们不在同一位置。
// 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。
// 迷宫至少包括2块空地,行数和列数均不超过100。
//
// Related Topics 深度优先搜索 广度优先搜索
// 60 0
题解
public int shortestDistance(int[][] maze, int[] start, int[] destination) {
// 迷宫的长宽
int row = maze.length;
int col = maze[0].length;
// 遍历的方向
int[] dx = new int[]{-1, 0, 1, 0};
int[] dy = new int[]{0, 1, 0, -1};
int[][] visited = new int[row][col];
// 优先队列, 按照路径长度进行升序排列
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
queue.add(new int[]{0, start[0], start[1]});
while (!queue.isEmpty()) {
int[] cur = queue.poll();
// 如果转折点已经被访问过, 则跳过
if (visited[cur[1]][cur[2]] == 1) {
continue;
}
// 将该转折点赋值为已访问
visited[cur[1]][cur[2]] = 1;
// 如果当前元素的坐标等于目标坐标, 则直接返回当前元素的距离值
if (Arrays.equals(new int[]{cur[1], cur[2]}, destination)) {
return cur[0];
}
// 从四个方向遍历
for (int d = 0; d < 4; d++) {
int x = cur[1];
int y = cur[2];
int dist = cur[0];
while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
x += dx[d];
y += dy[d];
dist++;
}
x -= dx[d];
y -= dy[d];
dist--;
if (visited[x][y] == 0) {
queue.add(new int[]{dist, x, y});
}
}
}
return -1;
}
题目
//由空地和墙组成的迷宫中有一个球。球可以向上(u)下(d)左(l)右(r)四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。迷宫中还有
//一个洞,当球运动经过洞时,就会掉进洞里。
//
// 给定球的起始位置,目的地和迷宫,找出让球以最短距离掉进洞里的路径。 距离的定义是球从起始位置(不包括)到目的地(包括)经过的空地个数。通过'u', 'd'
//, 'l' 和 'r'输出球的移动方向。 由于可能有多条最短路径, 请输出字典序最小的路径。如果球无法进入洞,输出"impossible"。
//
// 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。
//
//
//
// 示例1:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 球的初始位置 (rowBall, colBall) = (4, 3)
//输入 3: 洞的位置 (rowHole, colHole) = (0, 1)
//
//输出: "lul"
//
//解析: 有两条让球进洞的最短路径。
//第一条路径是 左 -> 上 -> 左, 记为 "lul".
//第二条路径是 上 -> 左, 记为 'ul'.
//两条路径都具有最短距离6, 但'l' < 'u',故第一条路径字典序更小。因此输出"lul"。
//
//
//
// 示例 2:
//
// 输入 1: 迷宫由以下二维数组表示
//
//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
//
//输入 2: 球的初始位置 (rowBall, colBall) = (4, 3)
//输入 3: 洞的位置 (rowHole, colHole) = (3, 0)
//
//输出: "impossible"
//
//示例: 球无法到达洞。
//
//
//
//
//
// 注意:
//
//
// 迷宫中只有一个球和一个目的地。
// 球和洞都在空地上,且初始时它们不在同一位置。
// 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。
// 迷宫至少包括2块空地,行数和列数均不超过30。
//
// Related Topics 深度优先搜索 广度优先搜索
// 35 0
题解
public class MazeIII {
@Test
public void test(){
int[][] maze = {
{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}
};
String b = findShortestWay(maze, new int[]{4, 3}, new int[]{0, 0});
System.out.println(b);
}
public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
int row = maze.length;
int col = maze[0].length;
int[] dx = new int[]{-1, 0, 1, 0};
int[] dy = new int[]{0, 1, 0, -1};
char[] chars = new char[]{'u', 'r', 'd', 'l'};
int min_size = Integer.MAX_VALUE;
String res = "impossible";
int[][] visited = new int[row][col];
// 优先队列, 按照dist升序排序
PriorityQueue<Position> queue = new PriorityQueue<>(new Comparator<Position>() {
@Override
public int compare(Position o1, Position o2) {
return o1.dist - o2.dist;
}
});
// 插入起始坐标
queue.add(new Position(ball[0], ball[1], 0, ""));
while (!queue.isEmpty()) {
Position cur = queue.poll();
// 如果当前dist已经超过了最小距离, 则直接跳过
if (cur.dist > min_size) {
continue;
}
// 如果当前位置是终点
if (Arrays.equals(new int[]{cur.x, cur.y}, hole)) {
// 如果当前dist小于最小距离, 则将最小距离替换为当前距离, 则更换路径
if (cur.dist < min_size) {
min_size = cur.dist;
res = cur.path;
} else if (cur.dist == min_size && cur.path.compareTo(res) < 0) {
// 如果当前dist等于最小距离, 且当前路径的字典小于最短路径的字典, 则更换路径
res = cur.path;
}
}
// 当前坐标标记已访问
visited[cur.x][cur.y] = 1;
// 沿四个方向遍历
for (int d = 0; d < 4; d++) {
int x = cur.x;
int y = cur.y;
int dist = cur.dist;
String path = cur.path;
path += chars[d];
while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
if (Arrays.equals(new int[]{x, y}, hole)) {
x += dx[d];
y += dy[d];
dist++;
break;
}
x += dx[d];
y += dy[d];
dist++;
}
x -= dx[d];
y -= dy[d];
dist--;
if (visited[x][y] == 0) {
queue.add(new Position(x, y, dist, path));
}
}
}
return res;
}
}
class Position {
int x;
int y;
int dist;
String path;
public Position(int x, int y, int dist, String path) {
this.x = x;
this.y = y;
this.dist = dist;
this.path = path;
}
}