文章目录
数字华容道——leetcode773
问题描述
在一个 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] 的排列.
分析
问题的本质是图的搜索,要求移动次数最少那么就是BFS搜索,例如board = [[1,2,3],[4,0,5]]把这个数组状态压缩成123405字符串,从这个字符串一步一步bfs转移成123450(0可以和2,4,5交换,可以发现和5交换一步就完成了)
代码
我在题目要求上把2和3抽取出来,再求解了最短路径
package com.lry.basic.algorithm.graph;
import java.util.*;
public class SlidingPuzzle {
int m=3;
int n=3;
Map<String,String> pre = new HashMap<>();
String end = null;
String initState;
public int slidingPuzzle(int[][] board) {
Queue<String> queue = new LinkedList<>();
//<"123540",0>value是步数
Map<String,Integer> visited = new HashMap<>();
initState = boardToStr(board);
if(initState.equals(endStr())){
end = initState;
return 0;
}
queue.offer(initState);
visited.put(initState,0);
while(!queue.isEmpty()){
String curStr = queue.poll();
List<String> nextList = getNext(curStr);
for(String next:nextList){
if(!visited.containsKey(next)){
visited.put(next,visited.get(curStr)+1);
queue.offer(next);
//保存路径
pre.put(next,curStr);
if(next.equals(endStr())){
end = next;
return visited.get(next);
}
}
}
}
return -1;
}
private String endStr(){
int mn = m*n;
StringBuilder sb = new StringBuilder();
for(int i=1;i<mn;i++){
sb.append(i);
}
sb.append(0);
return sb.toString();
}
private List<String> getNext(String curStr) {
List<String> res = new ArrayList<>();
int[][] board = strToBoard(curStr);
int zero = 0;
for(;zero<curStr.length();zero++){
if(board[zero/n][zero%n]==0)break;
}
int zeroX = zero/n;
int zeroY = zero%n;
for(int i=-1;i<=1;i++){
for(int j=-1;j<=1;j++){
if(i==0||j==0){
int x = zeroX+i;
int y = zeroY+j;
if(inArea(x,y)){
swap(board,zeroX,zeroY,x,y);
res.add(boardToStr(board));
swap(board,zeroX,zeroY,x,y);
}
}
}
}
return res;
}
private Iterable<String> path(){
List<String>res = new ArrayList<>();
if(end==null){
return res;
}
String cur = end;
while(cur!=initState){
res.add(0,cur);
cur = pre.get(cur);
}
res.add(0,initState);
return res;
}
private void swap(int[][] board, int zeroX, int zeroY, int x, int y) {
int t = board[zeroX][zeroY];
board[zeroX][zeroY] = board[x][y];
board[x][y] = t;
}
private boolean inArea(int x, int y) {
return x>=0&&x<=m-1&&y>=0&&y<=n-1;
}
private int[][] strToBoard(String curStr){
int[][] board = new int[m][n];
for(int i=0;i<curStr.length();i++){
board[i/n][i%n] = curStr.charAt(i)-'0';
}
return board;
}
private String boardToStr(int[][] board){
StringBuilder sb = new StringBuilder();
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
sb.append(board[i][j]);
}
}
return sb.toString();
}
public static void main(String[] args) {
SlidingPuzzle puzzle = new SlidingPuzzle();
System.out.println(puzzle.slidingPuzzle(new int[][]{{4,1,2},{5,0,3},{6,7,8}}));
System.out.println(puzzle.path());
}
}