[英]How to find the shortest path in a puzzle with keys and doors
我尝试了下面的代码,但它没有给我正确的答案。
这是问题陈述。
假设您有一个二维网格。 每个点要么是陆地,要么是水。 还有一个起点和一个目标。
现在有打开门的钥匙。 每把钥匙对应一扇门。
实现一个函数,使用地块、钥匙和打开的门返回从起点到目标的最短路径。
数据表示
地图将作为字符串数组传递。
地图可以具有以下图块。
0 = Water
1 = Land
2 = Start
3 = Goal
uppercase = door
lowercase = key
网格可以是这样的:
{{'0', '2', '1', '1', '1'}, {'0', '1', '0', '0', '1'}, {'0', '0', '0', '0', '1'}, {'0', '0', 'A', '0', '1'}, {'1', '1', 'a', '1', '1'}, {'1', 'b', '0', '0', 'B'}, {'1', '1', '0', '0', '1'}, {'0', '1', '0', '0', '3'}};
所以最短路径应该是:01->02->03->04->14->24->34->44->43->42->41->51->41->42->43 ->44->54->64->74
我的代码如下:
public class shortestPath {
static int shortest = Integer.MAX_VALUE;
static String path = "";
static ArrayList<ArrayList<int[]>> res = new ArrayList<ArrayList<int[]>>();
public static void main(String[] args) {
char[][] board = {{'0', '2', '1', '1', '1'},
{'0', '1', '0', '0', '1'},
{'0', '0', '0', '0', '1'},
{'0', '0', 'A', '0', '1'},
{'1', '1', 'a', '1', '1'},
{'1', 'b', '0', '0', 'B'},
{'1', '1', '0', '0', '1'},
{'0', '1', '0', '0', '3'}};
System.out.println(findShortest(board));
System.out.println(path);
}
public static int findShortest(char[][] board) {
if (board == null || board.length == 0 || board[0].length == 0) return 0;
int row = board.length;
int col = board[0].length;
int[] start = new int[2];
int[] end = new int[2];
int[][] visited = new int[row][col];
HashMap<Character, ArrayList<int[]>> hm = new HashMap<>();
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == '2') {
start = new int[]{i, j};
}
if (board[i][j] == '3') {
end = new int[]{i, j};
}
if (Character.isUpperCase(board[i][j])) {
char key = Character.toLowerCase(board[i][j]);
if (hm.containsKey(key)) {
hm.get(key).add(new int[]{i, j});
} else {
ArrayList<int[]> al = new ArrayList<>();
al.add(new int[]{i, j});
hm.put(key, al);
}
board[i][j] = '0';
}
}
}
//HashSet<Character> hs = new HashSet<Character>();
//helper2(board, hs, start[0], start[1], row, col, 0, visited, new StringBuilder(), new ArrayList<int[]>(), res);
helper(board, hm, start[0], start[1], row, col, 0, visited, new StringBuilder());
return shortest;
}
public static void helper(char[][] board, HashMap<Character, ArrayList<int[]>> hm, int r, int c, int row, int col, int count, int[][] visited, StringBuilder sb) {
// System.out.println(r + " " + c);
visited[r][c] = visited[r][c] + 1;
sb.append(r).append(c).append("->");
if (board[r][c] == '3') {
System.out.println(count);
if (shortest > count) {
path = sb.deleteCharAt(sb.length() - 1).deleteCharAt(sb.length() - 1).toString();
sb.append("->");
shortest = count;
}
//return;
//shortest = Math.min(shortest, count);
}
if (Character.isLowerCase(board[r][c])) { // if this is the key;
if (hm.containsKey(board[r][c])) {
for (int[] i : hm.get(board[r][c])) {
board[i[0]][i[1]] = '1';
}
}
}
if (r + 1 < row && board[r + 1][c] != '0' && visited[r + 1][c] < 2) {
helper(board, hm, r + 1, c, row, col, count + 1, visited, sb);
}
if (c + 1 < col && board[r][c + 1] != '0' && visited[r][c + 1] < 2) {
helper(board, hm, r, c + 1, row, col, count + 1, visited, sb);
}
if (r - 1 >= 0 && board[r - 1][c] != '0' && visited[r - 1][c] < 2) {
helper(board, hm, r - 1, c, row, col, count + 1, visited, sb);
}
if (c - 1 >= 0 && board[r][c - 1] != '0' && visited[r][c - 1] < 2) {
helper(board, hm, r, c - 1, row, col, count + 1, visited, sb);
}
visited[r][c] = visited[r][c] - 1;
sb.delete(sb.length() - 4, sb.length());
if (Character.isLowerCase(board[r][c])) { // if this is the key;
if (hm.containsKey(board[r][c])) {
for (int[] i : hm.get(board[r][c])) {
board[i[0]][i[1]] = '0';
}
}
}
return;
}
}
您的问题是visited
跟踪和锁门跟踪的错误实现的组合(由hm
变量管理,这是一个非常糟糕的名称,因为该名称无助于了解变量是/做什么) 。
访问跟踪
你的小机器人经常来回走来走去。 例如,如果您插入用于调试的打印语句:
您会得到以下结果,其中!
表示走了更长的路径, *
表示找到的目标路径更短:
1: ! 01->
2: ! 01->11->
3: ! 01->11->01->
4: ! 01->11->01->11->
6: ! 01->11->01->02->03->
7: ! 01->11->01->02->03->04->
8: ! 01->11->01->02->03->04->14->
9: ! 01->11->01->02->03->04->14->24->
10: ! 01->11->01->02->03->04->14->24->34->
11: ! 01->11->01->02->03->04->14->24->34->44->
12: ! 01->11->01->02->03->04->14->24->34->44->34->
13: ! 01->11->01->02->03->04->14->24->34->44->34->44->
14: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->
15: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->
16: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->
17: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->
18: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->32->
20: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->
21: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->
22: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->
23: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->
24: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->71->
26: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->
27: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->40->
28: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->40->50->
29: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->40->50->60->
30: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->40->50->60->50->
31: ! 01->11->01->02->03->04->14->24->34->44->34->44->43->42->43->42->41->51->61->71->61->51->41->40->50->60->50->60->
9577: * 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->71->61->51->41->42->43->44->54->64->74->
9611: ! 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->
9612: ! 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->
9613: ! 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->74->
9623: ! 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->34->24->14->04->03->02->
9901: * 01->11->01->02->03->04->14->24->34->44->43->42->41->51->61->51->41->42->43->44->54->64->74->
19141: ! 01->11->01->02->03->04->14->24->34->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->74->
53941: ! 01->11->01->02->03->04->14->04->14->24->34->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->
53942: ! 01->11->01->02->03->04->14->04->14->24->34->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->74->
145776: ! 01->11->01->02->03->02->03->04->14->04->14->24->34->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->
145777: ! 01->11->01->02->03->02->03->04->14->04->14->24->34->24->34->44->43->42->41->51->61->71->61->51->50->60->50->40->41->42->43->44->54->64->74->64->74->
158937: * 01->02->03->04->14->24->34->44->43->42->41->51->61->51->41->42->43->44->54->64->74->
采取的前进步骤总数: 390236
最长的路径来回很多:
01->11->
01->02->03->
02->03->04->14->
04->14->24->34->
24->34->44->43->42->41->51->61->71->61->
51->50->60->
50->40->41->42->43->44->54->64->74->
64->74->
那是很多浪费的步行。
锁门跟踪
回顾一下您的逻辑:您最初将所有门更改为0
(水)。 当您遇到钥匙时,您可以通过将板值更改为1
(地)来解锁所有适合该钥匙的门。 回溯时,您将所有门都改回0
(水)。
现在看看你的代码提出的解决方案:
01->02->03->04->14->24->34->44->43->42->41->51->61->
51->41->42->43->44->54->64->74
问题是关键是在51
,所以当代码从第二个51
解决方案回溯时,它关闭了门,这样当它回到第一个51
并试图从那里直接走到目标时,门已关闭,即使您有钥匙。
那是因为你没有意识到你有两次钥匙。
解决方案
如果您只是修复访问过的跟踪,您的代码将起作用,因为您不会两次踩到同一个键。
如果您只是修复锁定的门跟踪,即跟踪您拥有同一把钥匙的多个副本,您的代码将起作用,因为您不会再次过早地锁门。
但是,请考虑以下修改后的解决方案:
因此,以不同方式执行此操作的一种方法是考虑它在现实生活中的工作方式。 当您找到钥匙时,您不会打开门,因为 1) 您不一定知道门在哪里,以及 2) 您无法从站立的位置到达门。
相反,保留一个钥匙圈。 当您遇到没有的钥匙时,请将其添加到钥匙圈中。 将新钥匙添加到钥匙圈也意味着您可以重新访问以前走过的区域。 当你遇到一扇门时,如果你有钥匙圈里的钥匙,你就可以穿过它。
由于有 26 个可能的密钥,一个 32 位的int
值可以作为您的密钥环。
请参阅IDEONE以获取仅需要 90 步(不是当前代码的 390236 步)来检查所有可能/相关路径的示例实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.