LeetCode——滑动谜题
作者:互联网
Q:在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.一次移动定义为选择 0 与一个相邻的数字(上下左右)进行交换.最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。
给出一个谜板的初始状态,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。
示例:
输入:board = [[1,2,3],[4,0,5]]
输出:1
解释:交换 0 和 5 ,1 步完成
输入:board = [[1,2,3],[5,4,0]]
输出:-1
解释:没有办法完成谜板
输入:board = [[4,1,2],[5,0,3]]
输出:5
解释:
最少完成谜板的最少移动次数是 5 ,
一种移动路径:
尚未移动: [[4,1,2],[5,0,3]]
移动 1 次: [[4,1,2],[0,5,3]]
移动 2 次: [[0,1,2],[4,5,3]]
移动 3 次: [[1,0,2],[4,5,3]]
移动 4 次: [[1,2,0],[4,5,3]]
移动 5 次: [[1,2,3],[4,5,0]]
输入:board = [[3,2,4],[1,5,0]]
输出:14
提示:
board 是一个如上所述的 2 x 3 的数组.
board[i][j] 是一个 [0, 1, 2, 3, 4, 5] 的排列.
A:(引用自:labuladong)
对于这种计算最小步数的问题,我们就要敏感地想到 BFS 算法。
这个题目转化成 BFS 问题是有一些技巧的,我们面临如下问题:
1、一般的 BFS 算法,是从一个起点start开始,向终点target进行寻路,但是拼图问题不是在寻路,而是在不断交换数字,这应该怎么转化成 BFS 算法问题呢?
2、即便这个问题能够转化成 BFS 问题,如何处理起点start和终点target?
首先回答第一个问题,BFS 算法并不只是一个寻路算法,而是一种暴力搜索算法,只要涉及暴力穷举的问题,BFS 就可以用,而且可以最快地找到答案。明白了这个道理,我们的问题就转化成了:如何穷举出board当前局面下可能衍生出的所有局面?这就简单了,看数字 0 的位置呗,和上下左右的数字进行交换就行了:
这样其实就是一个 BFS 问题,每次先找到数字 0,然后和周围的数字进行交换,形成新的局面加入队列…… 当第一次到达target时,就得到了赢得游戏的最少步数。
对于第二个问题,我们这里的board仅仅是 2x3 的二维数组,所以可以压缩成一个一维字符串。其中比较有技巧性的点在于,二维数组有「上下左右」的概念,压缩成一维后,如何得到某一个索引上下左右的索引?很简单,我们只要手动写出来这个映射就行了:
List<List<Integer>> neighbor = new ArrayList<>();
neighbor.add(Arrays.asList(1, 3));
neighbor.add(Arrays.asList(0, 4, 2));
neighbor.add(Arrays.asList(1, 5));
neighbor.add(Arrays.asList(0, 4));
neighbor.add(Arrays.asList(3, 1, 5));
neighbor.add(Arrays.asList(4, 2));
这个含义就是,在一维字符串中,索引i在二维数组中的的相邻索引为neighbor[i]:
至此,我们就把这个问题完全转化成标准的 BFS 问题了。
public int slidingPuzzle(int[][] board) {
int m = 2, n = 3;
StringBuilder start = new StringBuilder();
String target = "123450";
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
start.append(board[i][j]);
}
}
//每个位置的邻居位置
List<List<Integer>> neighbor = new ArrayList<>();
neighbor.add(Arrays.asList(1, 3));
neighbor.add(Arrays.asList(0, 4, 2));
neighbor.add(Arrays.asList(1, 5));
neighbor.add(Arrays.asList(0, 4));
neighbor.add(Arrays.asList(3, 1, 5));
neighbor.add(Arrays.asList(4, 2));
Queue<StringBuilder> q = new LinkedList<>();
//防止走回头路
Set<String> visited = new HashSet<>();
q.add(start);
visited.add(start.toString());
int step = 0;
while (!q.isEmpty()) {
int sz = q.size();
for (int i = 0; i < sz; i++) {
StringBuilder cur = q.poll();
String curr = cur.toString();
if (curr.equals(target)) {
return step;
}
//找0的位置
int idx = 0;
for (; cur.charAt(idx) != '0'; idx++) ;
//对0位置的每个邻居交换
for (int adj : neighbor.get(idx)) {
char[] new_board = curr.toCharArray();
char temp = new_board[adj];
new_board[adj] = new_board[idx];
new_board[idx] = temp;
String new_n_board = new String(new_board);
//没走过的路加入
if (!visited.contains(new_n_board)) {
q.add(new StringBuilder(new_n_board));
visited.add(new_n_board);
}
}
}
step++;
}
return -1;
}
标签:Arrays,谜题,add,board,neighbor,new,滑动,LeetCode,asList 来源: https://www.cnblogs.com/xym4869/p/13071645.html