leetcode773 - Sliding Puzzle - hard
On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and an empty square represented by 0.
A move consists of choosing 0 and a 4-directionally adjacent number and swapping it.
The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]].
Given a puzzle board, return the least number of moves required so that the state of the board is solved. If it is impossible for the state of the board to be solved, return -1.
Examples:
Input: board = [[1,2,3],[4,0,5]]
Output: 1
Explanation: Swap the 0 and the 5 in one move.
Input: board = [[1,2,3],[5,4,0]]
Output: -1
Explanation: No number of moves will make the board solved.
Input: board = [[4,1,2],[5,0,3]]
Output: 5
Note:
* board will be a 2 x 3 array as described above.
* board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5].
层级遍历的BFS。
算法原因:一层层扩展出去的找答案,找到答案的瞬间一定是最近的方法。DFS也有办法搜到答案,但你不能保证你搜到的那个是最短的,你得搜完所有可能的答案并且每次搜到的时候打一次擂台才能保证正确性,时间复杂度会不如BFS。
算法辅助:需要用一个标识符标识状态,加入set从而避免重复搜索。本题不能直接保存int[][]格式的board,你可以转化为String后放入set。所以要有string和board的来回转换。
细节:
1.层级遍历的while循环内最后一行别忘了写step++,一写长就忘,很多次了。
2.在上下左右去尝试swap的时候,swap完别忘了换回去,这题要回溯,因为你四次尝试swap的对象board是同一个int[][],不像其他题目BFS是比较简单的数据结构。
3.取出一个crt以后去上下左右尝试动0,swap的时候注意swap对象是crt不是整个函数传入的board。对当前这一步进行修改,而不是算出一个0的位置后拿着这个位置回到最初的棋盘上乱改。
实现:
class Solution { private class Pair { public int x; public int y; public Pair(int x, int y) { this.x = x; this.y = y; } } public int slidingPuzzle(int[][] board) { int[] dx = {0, 1, 0, -1}; int[] dy = {-1, 0, 1, 0}; Queue<String> q = new LinkedList<>(); Set<String> set = new HashSet<>(); String s = boardToString(board); q.offer(s); set.add(s); int step = 0; while (!q.isEmpty()) { int size = q.size(); for (int round = 0; round < size; round++) { s = q.poll(); if (isSolved(s)) { return step; } int[][] crt = stringToBoard(s); Pair zeroIdx = findZero(crt); int x = zeroIdx.x, y = zeroIdx.y; for (int i = 0; i < 4; i++) { int nx = x + dx[i]; int ny = y + dy[i]; if (!isValid(crt, nx, ny)) { continue; } // P3: 这里要swap的对象是crt不是board!!!你要对你提取出来的这一步的棋盘做修改啊不是最开始的。 swap(crt, x, y, nx, ny); String ns = boardToString(crt); if (!set.contains(ns)) { set.add(ns); q.offer(ns); } // P2: 这里千万记住要回溯回去!!!!因为你一直在用同一个crt不是新建!不回去你下一次循环会错的! swap(crt, x, y, nx, ny); } } // P1: 别忘了层级遍历的增加层数!!!!! step++; } return -1; } private String boardToString(int[][] board) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 6; i++) { sb.append(board[i / 3][i % 3]); } return sb.toString(); } private int[][] stringToBoard(String s) { int[][] ans = new int[2][3]; for (int i = 0; i < 6; i++) { ans[i / 3][i % 3] = s.charAt(i) - '0'; } return ans; } private Pair findZero(int[][] board) { int x = 0, y = 0; for (int i = 0; i < 6; i++) { if (board[i / 3][i % 3] == 0) { x = i / 3; y = i % 3; } } return new Pair(x, y); } private boolean isSolved(String s) { return "123450".equals(s); } private boolean isValid(int[][] board, int x, int y) { return x >= 0 && x < board.length && y >= 0 && y < board[0].length; } private void swap(int[][] board, int x1, int y1, int x2, int y2) { int temp = board[x1][y1]; board[x1][y1] = board[x2][y2]; board[x2][y2] = temp; } }