输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
算法思路:
通过移动x的位置,找出到达终点状态的最少次数,属于权值为1的最短路问题,用宽搜。
由于是从初始状态的图到终止状态的图,所以需要将八数码的所有状态抽象成图中的一个结点。
- 状态表示:八数码是3x3的矩阵,可以将二维矩阵转换为一维的字符串,用字符串存储状态。
- 记录到达每个状态的移动次数:由于不能直接用数组表示距离,所以可以用map记录,key存储每个状态,value存储到达每个状态的次数。若某个状态的value为空,则表示该状态还没有被访问过。
- 判断从一个状态到达另一个状态:先将出队的状态转换为二维坐标,加上偏移量后再转换回一维字符串。
Java代码:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
String st = "";
for(int i = 0; i < split.length; i++)
st += split[i];
String end = "12345678x";
System.out.println(bfs(st, end));
}
private static void swap(char arr[], int a, int b) { //交换数组中的ab位置元素,数组为引用数据类型(引用传递)
char t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
public static int bfs(String st, String end) {
Queue<String> qu = new LinkedList<>();
Map<String,Integer> map = new HashMap<>(); // 记录到达某个状态时的次数
map.put(st, 0); // 开始状态次数为0
qu.add(st);
int []dx = new int[] {-1, 0, 1, 0};
int []dy = new int[] {0, 1, 0, -1};
while(!qu.isEmpty()) {
String str = qu.poll();
if(str.equals(end)) return map.get(str); //到达终止状态时,返回此时次数
int idx = str.indexOf('x'); //找出一维数组中x的位置
int x = idx / 3, y = idx % 3; // 一维数组转化成二维数组
for(int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i]; // 加上偏移量得到新的状态
if(a < 0 || a > 2 || b < 0 || b > 2) continue; // 不合法状态
char []arr = str.toCharArray(); // 将字符串转化成数组
swap(arr, idx, 3 * a + b); // 改变状态
String after = new String(arr); // 再转化回去
if(map.get(after) == null) { // 当某个转台还没有被访问时
map.put(after, map.get(str)+ 1); // 将次数存储下来
qu.add(after); // 将这个状态入队
}
}
}
return -1;
}
}