原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=1043
这是一道经典的八数码问题,题目给定一个初始状态,要求将这个初始状态转换为目标状态的步骤,目标状态都是一样的,都为1 2 3 4 5 6 7 8 x,注意这道题是Special Judge,即转换的步骤其实是不止一种,而这里只要求输出一种方案即可。
这道题目的解决方案不止一种,事实上有大神总结出了一共有八种方案,详见这里,当然这些方案中有的是铁定TLE的,而有的是小的优化,而我这里主要实现了其中的三种,分别是BFS+Hash+打表,双向BFS+Hash,A*+Hash+曼哈顿距离+优先队列
BFS这里就不对其进行赘述了,如果不清楚的请看这里,而对于Hash我想也没必要进行太多的讲解,其实很简单,当我们对所有解空间进行搜索的时候,每搜索到一种新的状态我们都需要对其进行保存,而hash就是用来对每一个搜索到的状态进行一个唯一的编码,例如我们搜索到一个为1 2 3 4 5 6 7 8 x
的状态,我们要将其进行保存,以便后续的访问,如果保存在list中的话查找十分麻烦,而如果存在数组中,那我们就需要一种编码方式,对该状态进行唯一的标识,这就是hash函数的作用,而hash函数是有很多种,所以我们就需要一种最为合适的。
在该题中,对于每一个状态,如果我们都将x视为0的话,那么这就是一个从0到8的全排列,既然是全排列的话那就简单了,我们可以直接用该数字作为一个hash编码方式,例如状态为1 2 3 4 5 6 7 8 0
就直接保存在数组中该数字对应的位置上,但这就会导致大量的空间浪费,那么我们就需要另一种更优的hash编码方式,我们可以用每一个排列在所有全排列中的位置作为hash编码,例如:0 1 2 3 4 5 6 7 8
在所有全排列中的位置是1,那就将该状态存在数组1这个位置上。 然而随之而来的问题是怎么求这个位置,此时我们就需要引入一个概念,即康托展开。
康拓展开定义:
计算公式:X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!
注:X为比该数小的个数,其中ai为当前元素在未出现的元素中是排在第几个(从0开始),n指的是该数的位数
例:比如2143 这个数,求其展开:
从头判断,至尾结束,注意顺序是从左至右。
① 比 2(第一位数)小的数有多少个->1个,换而言之就是2排在第1位,因为第0位排的是1,所以ai就是1,又因为该数一共4位,所以n是4,因此这里ai*(n-1)! -> 1*3!=6
② 比 1(第二位数)小的数有多少个->0个0*2!=0
③ 比 4(第三位数)小的数有多少个->3个就是1,2,3,但是1,2之前已经出现,所以是1*1!=1
将所有乘积相加 6+0+1=7
比该数小的数有7个,所以该数排第8的位置。
1234 1243 1324 1342 1423 1432
2134 2143 2314 2341 2413 2431
3124 3142 3214 3241 3412 3421
4123 4132 4213 4231 4312 4321
康托展开代码实现:
private static int Cantor(int[] a) {//计算该状态的康托展开
int res = 0;
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};//0到9各个数的阶乘
for (int i = 0; i < a.length; i++) {
int temp = 0;//比a[i]小的数的个数
for (int j = i + 1; j < a.length; j++) {
if (a[i] > a[j]) temp++;//在i之前出现过比a[i]小的数要去掉
}
res += temp * fac[a.length - i - 1];//计算
}
return res + 1;//返回a在全排列中排第几位
}
到这里,我们其实已经可以对该题进行解答了,我们使用BFS对解答树进行搜索,每搜索到一个新的状态就求其康拓展开,将到达该状态的路径保存到对应的数组中直到搜索到了目标状态1 2 3 4 5 6 7 8 x
,但这就会出现一个效率问题,因为该题有N组测试数据,这就导致了我们对每一组测试数据都要重新进行计算,很明显会TLE,这就需要用到一个信息学竞赛的技巧,即打表,所谓打表就是提前对问题所有的解进行求解,并保存在内存中,之后对于每一组测试用例直接返回之前提前解答的结果。
但又有一个问题就是我们并不知道每一次询问的初始状态是什么,那怎么进行打表呢?这也很简单,初始状态我们是不知道,但目标状态呢?这就是我们要进行打表的对象。我们以目标状态作为初始状态进行逆向打表,这样在每一次获得初始状态时就可以直接输出结果。
AC代码:
import java.io.BufferedInputStream;
import java.util.ArrayDeque;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
*
* @author wanyu
* @Date: 2018-01-31
* @Time: 15:09
* To change this template use File | Settings | File Templates.
* @desc 逆向打表+BFS+哈希
*/
public class Main {
private static int[] move = {-1, -3, 1, 3};//移动数组
private static int[] map;//存储八数码
private static boolean[] visited;//判断是否已经访问过
private static String[] path = new String[363000];//保存已经遍历过的状态,使用哈希进行空间压缩
private static void init(Scanner in) {//初始化信息
String[] line = in.nextLine().split("");//读取输入
map = new int[9];
int j = 0;
for (int i = 0; i < line.length; i++) {
if (line[i].equals(" ")) continue;
if (line[i].equals("x")) {
map[j++] = 0;//对X进行转换
continue;
}
map[j++] = Integer.parseInt(line[i]);
}
}
private static void create() {//进行打表操作
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 0};//以目标状态作为初始状态进行逆向打表
int cantor = Cantor(num);//计算康拓展开
path[cantor] = "lr";//初始状态
visited = new boolean[363000];//用于判重
Node node = new Node(num, new StringBuffer(""), cantor, 8);//初始节点
BFS(node);
}
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
create();//进行打表
while (in.hasNext()) {
init(in);//初始化
int cantor = Cantor(map);//计算初始状态的康托
if (path[cantor] == null) {//如果没有到该状态的路径表示无解
System.out.println("unsolvable");
continue;
}
StringBuffer stringBuffer = new StringBuffer(path[cantor]);
//注意因为是以目标状态作为初始状态进行打表的
// 所以该路径是从目标状态到初始状态的,所以在输出时需要进行反转
System.out.println(stringBuffer.reverse());
}
}
private static void BFS(Node start) {//使用BFS进行解答树的搜索
ArrayDeque queue = new ArrayDeque<>();
queue.add(start);
while (!queue.isEmpty()) {
Node node = queue.poll();//抛出队首元素
for (int i = 0; i < 4; i++) {//对该节点进行扩展
int index = node.index;
int new_index = index + move[i];//确定x下一个位置
//判断当前位置是否可以移动
if ((index == 2 || index == 5 || index == 8) && i == 2) continue;
if ((index == 0 || index == 3 || index == 6) && i == 0) continue;
if (new_index >= 0 && new_index <= 8) {//边界处理
Node new_node = new Node(node.state, node.path, node.index, node.cantor);//定义新的节点
//更新数据
new_node.state[index] = new_node.state[new_index];//直接赋值
new_node.state[new_index] = 0;
new_node.index = new_index;
int cantor = Cantor(new_node.state);//计算该状态的康托展开
if (visited[cantor]) continue;//如果该状态已经访问过,直接进入下一个状态
new_node.cantor = cantor;
visited[cantor] = true;//标记该状态已经访问过了
switch (move[i]) {//添加路径
case 1:
new_node.path.append("l");
break;
case -1:
new_node.path.append("r");
break;
case -3:
new_node.path.append("d");
break;
case 3:
new_node.path.append("u");
break;
default:
break;
}
path[cantor] = new_node.path.toString();//保存路径信息
queue.add(new_node);//将该节点添加到队列中
}
}
}
}
private static int Cantor(int[] a) {//计算该状态的康托展开
int res = 0;
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};//0到9各个数的阶乘
for (int i = 0; i < a.length; i++) {
int temp = 0;//比a[i]小的数的个数
for (int j = i + 1; j < a.length; j++) {
if (a[i] > a[j]) temp++;//在i之前出现过比a[i]小的数要去掉
}
res += temp * fac[a.length - i - 1];//计算
}
return res + 1;//返回a在全排列中排第几位
}
}
class Node {
int[] state;//当前状态
StringBuffer path;//路径
int index;//x的位置
int cantor;//康托展开
public Node(int[] state, StringBuffer path, int cantor, int index) {
this.state = new int[state.length];
System.arraycopy(state, 0, this.state, 0, state.length);
this.path = new StringBuffer(path);
this.index = index;
this.cantor = cantor;
}
}
双向BFS思路其实和单向BFS一样,只不过这里从两个节点开始搜索,即同时从初始状态和目标状态进行搜索,如果二者在中间状态相遇就表示找到了解,双向BFS相较于单向的BFS解空间减少了,所以搜索效率上优于单项的BFS。
图示:
然后此处还有一个小的优化点,在上一个方案中,我们对于无解的状态只需要直接判断是否能从目标状态搜索到,对于搜索不到的在path中直接保存为null即可,但现在我们没法这样做,此时我们就需要先判断该初始状态是否有解。对于判断八数码有无解就需要用到逆序对,具体的证明见:八数码有解证明,这里直接给出结论。
八数码问题的有解无解的结论:
将八数码的一个状态表示成一维的形式,求出除0之外所有数字的逆序数之和,也就是每个数字前面比它大的数字的个数的和,称为这个状态的逆序数和。
若两个状态的逆序奇偶性相同,则可相互到达,否则不可相互到达。
由于原始状态的逆序为0(偶数),则逆序为偶数的状态有解。
注:此处之所以将0作为偶数,是因为0%2=0,所以将之视为偶数。
AC代码:
import java.io.BufferedInputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
*
* @author wanyu
* @Date: 2018-02-02
* @Time: 18:10
* To change this template use File | Settings | File Templates.
* @desc HDU1043 双向广搜+HASH
*/
public class Main {
private static int[] move = {1, -3, -1, 3};//移动数组
private static int[] map;//存储八数码
private static boolean[] visited1;//判断从start扩展来的是否已经访问过
private static boolean[] visited2;//判断从end扩展来的是否已经访问过
private static String[] path;
private static int start = 0;//起始点
private static void init(Scanner in) {//初始化信息
String[] line = in.nextLine().split("");//读取输入
map = new int[9];
path = new String[363000];
visited1 = new boolean[363000];
visited2 = new boolean[363000];
int j = 0;
for (String aLine : line) {
if (aLine.equals(" ")) continue;
if (aLine.equals("x")) {
start = j;//起始点
map[j++] = 0;
continue;
}
map[j++] = Integer.parseInt(aLine);
}
}
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
while (in.hasNext()) {
init(in);//初始化
if (judge()) {//判断有无解
//状态可达
//进行双向广搜
Node snode = new Node(map, Cantor(map), start);//初始化开始节点
int[] num = {1, 2, 3, 4, 5, 6, 7, 8, 0};
int cantor = Cantor(num);
Node enode = new Node(num, cantor, 8);//初始化结束节点
BFS(snode, enode);
} else {
//状态不可达
System.out.println("unsolvable");
}
}
}
private static void BFS(Node start, Node end) {//双向广搜
//两个初始点已访问
visited1[start.cantor] = true;
visited2[end.cantor] = true;
//初始化两个节点的初始路径
path[start.cantor] = "";
path[end.cantor] = "";
Deque Squeue = new ArrayDeque<>();
Deque Equeue = new ArrayDeque<>();
//添加节点
Squeue.add(start);
Equeue.add(end);
while (!Squeue.isEmpty() && !Equeue.isEmpty()) {//两个同时不为空,同时对两个状态进行扩展
//先扩展初始状态
Node snode = Squeue.poll();
int Sindex = snode.index;
for (int i = 0; i < 4; i++) {
//边界情况处理
if ((Sindex == 0 || Sindex == 3 || Sindex == 6) && move[i] == -1) continue;
if ((Sindex == 2 || Sindex == 5 || Sindex == 8) && move[i] == 1) continue;
int newIndex = Sindex + move[i];//新的x的位置
if (newIndex >= 0 && newIndex <= 8) {//新的位置是合法的
Node newNode = new Node(snode.state, snode.cantor, snode.index);//初始化新的节点
newNode.state[Sindex] = newNode.state[newIndex];
newNode.state[newIndex] = 0;
newNode.index = newIndex;
int cantor = Cantor(newNode.state);
if (!visited1[cantor]) { //是否访问过了
String p = path[snode.cantor] + transform(move[i], 1);//暂时保存路径
newNode.cantor = cantor;
visited1[cantor] = true;//设为已访问
if (visited2[cantor]) {
//如果该节点两边都扩展到了,则表示已经找到了
System.out.println(p + new StringBuilder(path[cantor]).reverse());
//因为从目标状态过来的,所以需要将路径进行翻转操作
return;
}
path[cantor] = p;//保存路径
Squeue.add(newNode);//入队
}
}
}
//再扩展目标状态
Node enode = Equeue.poll();
int Eindex = enode.index;
for (int i = 0; i < 4; i++) {
//边界情况处理
if ((Eindex == 0 || Eindex == 3 || Eindex == 6) && move[i] == -1) continue;
if ((Eindex == 2 || Eindex == 5 || Eindex == 8) && move[i] == 1) continue;
int newIndex = Eindex + move[i];//新的x的位置
if (newIndex >= 0 && newIndex <= 8) {//新的位置是合法的
Node newNode = new Node(enode.state, enode.cantor, enode.index);//初始化新的节点
newNode.state[Eindex] = newNode.state[newIndex];
newNode.state[newIndex] = 0;
newNode.index = newIndex;
int cantor = Cantor(newNode.state);
if (!visited2[cantor]) { //是否访问过了
StringBuilder p = new StringBuilder(path[enode.cantor] + transform(move[i], 0));//暂时保存路径
newNode.cantor = cantor;
visited2[cantor] = true;//设为已访问
if (visited1[cantor]) {
//如果该节点两边都扩展到了,则表示已经找到了
System.out.println(path[cantor] + p.reverse());
return;
}
path[cantor] = p.toString();
Equeue.add(newNode);//入队
}
}
}
}
}
/**
* @param x 扩展方向
* @param state 从开始还是结尾扩展而来的
* @return 方向
*/
private static String transform(int x, int state) {
String res = "";
if (state == 1) {//开始节点
switch (x) {
case 1:
res = "r";
break;
case -1:
res = "l";
break;
case 3:
res = "d";
break;
case -3:
res = "u";
break;
}
} else {
switch (x) {
case 1:
res = "l";
break;
case -1:
res = "r";
break;
case 3:
res = "u";
break;
case -3:
res = "d";
break;
}
}
return res;
}
private static boolean judge() {//判断是否有解只需要判断逆序对的个数
int num = 0;//逆序数和
for (int i = 0; i < map.length - 1; i++) {
if (map[i] == 0) continue;//如果是0就跳过
for (int j = i + 1; j < map.length; j++) {
if (map[i] > map[j] && map[j] != 0) {
num++;
}
}
}
return (num & 1) == 0;//如果是偶数,表示该状态可达
}
private static int Cantor(int[] nums) {//计算康托展开式
int res = 0;
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};//0到9各个数的阶乘
for (int i = 0; i < nums.length - 1; i++) {
int sum = 0;//比nums[i]小的数
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[i]) sum++;
}
res += sum * fac[nums.length - i - 1];
}
return res + 1;
}
}
class Node {
int state[];//状态
int cantor;//hash值
int index;//x的位置
Node(int[] state, int cantor, int index) {
this.state = new int[state.length];
System.arraycopy(state, 0, this.state, 0, state.length);
this.cantor = cantor;
this.index = index;
}
}
A*算法是一类启发式搜索算法,相比于DFS或BFS这类盲目的搜索更具有了目的性,它对于节点的扩展具有选择性,它会优先选择距离目标状态更近的节点进行扩展。
BFS与A*比较
从图中可以看出A*比BFS更快速的找到了一条更好的路径。
对于A*算法,未曾接触过的人可能会觉得有些许复杂,但对于掌握的人来说其实非常简单,其实只要将BFS加上一个评估函数就是A*算法,然而虽然形式上非常简单,但是这个评估函数的选择却十分重要,如果选择了错误的评估函数就会导致无法求得结果,对于A*算法可以看这篇文章,我个人认为讲的还是十分通俗易懂的。
在这里我们A*算法的评估函数选用F=G+H
G:表示从初始状态到达当前状态的代价。
H:表示从当前状态到目标状态的估计代价,注意这是一个估计值而不是准确值,同时要注意该估计值需要小于等于准确值,虽然H值越大算法运行越快,但是如果大于准确值会漏掉最优解。
在该处我们选用曼哈顿距离作为H函数,即将1-8这八个数字的当前位置到目标位置的曼哈顿距离之和作为估计代价。
所谓曼哈顿距离就是指两个在平面坐标中的点,假设分别为A1(x1,y1)和A2(x2,y2),而曼哈顿距离等于|x1-x2|+|y1-y2|
。除了曼哈顿距离其实还有诸如切米雪夫距离,欧氏距离等等,这些不在我们的讨论范围内,想了解的可以看这里。
八数码曼哈顿距离计算:
例:
2 3 4 1 2 3
1 5 x ——> 4 5 6
7 6 8 7 8 x
数字 | 初始位置 | 目标位置 | 曼哈顿距离 |
---|---|---|---|
1 | (1,0) | (0,0) | 1 |
2 | (0,0) | (0,1) | 1 |
3 | (0,1) | (0,2) | 1 |
4 | (0,2) | (1,0) | 3 |
5 | (1,1) | (1,1) | 0 |
6 | (2,1) | (1,2) | 2 |
7 | (2,0) | (2,0) | 0 |
8 | (2,2) | (2,1) | 1 |
x | (1,2) | (2,2) | 1 |
综上所以该初始状态到目标状态的曼哈顿距离之和为:1+1+1+3+0+2+0+1+1=10
AC代码:
import java.io.BufferedInputStream;
import java.util.PriorityQueue;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
*
* @author wanyu
* @Date: 2018-02-05
* @Time: 17:07
* To change this template use File | Settings | File Templates.
* @desc A*+Hash
* 1.使用逆序对来判断是否有解
* 2.有解的话使用A*进行路径搜索
* 3.对于空间使用hash进行空间压缩
*/
public class Main {
private static int[] map;
private static int start;//起始点
private static String[] path;//从起点到终点的路径
private static int[] x = {0, 1, 2, 0, 1, 2, 0, 1, 2};//将一维数组转化为二维数组
private static int[] y = {0, 0, 0, 1, 1, 1, 2, 2, 2};//将一维数组转化为二维数组
private static boolean[] visited;
private static int[] move = {1, -1, 3, -3};//移动数组
private static int end;//结束状态
private static void init(Scanner in) {//初始化,读取数据
String[] line = in.nextLine().split("");
map = new int[9];
path = new String[363000];
visited = new boolean[363000];
int j = 0;
for (String aLine : line) {
if (aLine.equals(" ")) continue;
if (aLine.equals("x")) {
start = j;
map[j++] = 9;
continue;
}
map[j++] = Integer.parseInt(aLine);
}
}
private static void A_Star(Node1 start) {//使用A*算法求解路径
PriorityQueue queue = new PriorityQueue<>();//优先队列
queue.add(start);//加入初始节点
path[start.cantor] = "";//初始化节点路径
while (!queue.isEmpty()) {
Node1 temp = queue.poll();//从队列中弹出f值最小的节点
visited[temp.cantor] = true;
//对其周围进行遍历,将符合要求的节点加入到队列中
int index = temp.index;//x的位置
for (int i = 0; i < 4; i++) {
//确保边界
if ((index == 0 || index == 3 || index == 6) && i == 1) continue;//左边界
if ((index == 2 || index == 5 || index == 8) && i == 0) continue;//右边界
int new_index = index + move[i];//新的x的位置
if (new_index >= 0 && new_index <= 8) {
Node1 node = new Node1(new_index, temp.state, temp.cantor);//新的节点
node.state[index] = temp.state[new_index];
node.state[new_index] = 9;
node.index = new_index;
int cantor = cantor(node.state);//对应的hash值
node.cantor = cantor;
node.flush(temp.g + 1, h(node.state));//更新f值
if (!visited[cantor]) {//是否已经访问过了
queue.add(node);//入队
switch (move[i]) {//更新路径
case 1:
path[cantor] = path[temp.cantor] + "r";
break;
case -1:
path[cantor] = path[temp.cantor] + "l";
break;
case -3:
path[cantor] = path[temp.cantor] + "u";
break;
case 3:
path[cantor] = path[temp.cantor] + "d";
break;
}
if (cantor == end) {//是否找到节点了
System.out.println(path[cantor]);
return;
}
}
}
}
}
}
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
end = cantor(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
while (in.hasNext()) {
init(in);
if (judge()) {
//构造初始节点
Node1 node1 = new Node1(0, h(map), start, map, cantor(map));
if (node1.cantor == end) {//如果目标状态等于初始状态则直接输出
System.out.println("lr");
continue;
}
A_Star(node1);//A*
} else {
System.out.println("unsolvable");
}
}
}
//评估函数
private static int h(int[] a) {//估计从当前节点到终点的代价
//使用曼哈顿距离
int sum = 0;
for (int i = 0; i < 9; i++) {
int n = a[i] - 1;
int dis = Math.abs(x[i] - x[n]) + Math.abs(y[i] - y[n]);
sum += dis;
}
return sum;
}
private static boolean judge() {//判断是否有解
int sum = 0;
for (int i = 0; i < map.length; i++) {
if (map[i] == 9) continue;//跳过x的位置
for (int j = i + 1; j < map.length; j++) {
if (map[i] > map[j]) sum++;
}
}
return (sum & 1) == 0;
}
private static int cantor(int[] num) {
int sum = 0;
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};//0到9各个数的阶乘
for (int i = 0; i < 8; i++) {
int temp = 0;
for (int j = i + 1; j < 9; j++) {
if (num[i] > num[j]) temp++;
}
sum += temp * fac[8 - i];
}
return sum;
}
}
class Node1 implements Comparable {
//A*算法的评估函数F=G+H
//G表示从起点到当前节点的实际代价,H表示从当前节点到终点的估计代价
int f, g, h;//评估函数
int index;//x所在的位置
int state[];//当前节点的状态
int cantor;//hash值
Node1(int g, int h, int index, int[] state, int cantor) {
this.index = index;
this.cantor = cantor;
this.state = new int[state.length];
System.arraycopy(state, 0, this.state, 0, state.length);
flush(g, h);
}
public void flush(int g, int h) {
this.g = g;
this.h = h * 10;
this.f = g + h;//刷新f值
}
Node1(int index, int[] state, int cantor) {
this.index = index;
this.cantor = cantor;
this.state = new int[state.length];
System.arraycopy(state, 0, this.state, 0, state.length);
}
@Override
public int compareTo(Node1 n) {
return f - n.f;
}
}
八数码问题的解法除了以上的还有一种比较好的方法是IDA*算法,但IDA*算法用的比较少,所以这里就不做展开了。
此处我将我在解决该问题时写的自动生成测试用例的代码,以及根据结果自动求解的代码奉上。
测试用例自动生成
public static void main(String[] args) throws FileNotFoundException {
Scanner in = new Scanner(new BufferedInputStream(System.in));
File file = new File("C:\\Users\\wanyu\\Desktop\\Test.txt");//测试用例输出的路径,按需修改
PrintWriter printWriter = new PrintWriter(file);
int n = in.nextInt();//输入测试用例的个数
for (int i = 0; i < n; i++) {
boolean[] visted = new boolean[9];
StringBuffer stringBuffer = new StringBuffer();
int start = RandomUtils.nextInt(1, 9);//随机生成起点
for (int j = 0; j < 9; j++) {
if (j == start) {
stringBuffer.append("x ");
continue;
}
int x = RandomUtils.nextInt(1, 9);
while (visted[x]) x = RandomUtils.nextInt(1, 9);
visted[x] = true;
stringBuffer.append(x);
if (j != 8)
stringBuffer.append(" ");
}
stringBuffer.append("\n");
printWriter.append(stringBuffer);
}
printWriter.flush();
}
简易自动求解代码:
public static void main(String[] args) {
//输入初始状态和步骤,自动进行求解
Scanner in = new Scanner(new BufferedInputStream(System.in));
while (true) {
int now = 0;
int j = 0;
String map[] = new String[9];
String line1[] = in.nextLine().split("");
for (String aLine : line1) {
if (aLine.equals(" ")) continue;
if (aLine.equals("x")) {
now = j;//起始点
}
map[j++] = aLine;
}
String line = in.nextLine();//步骤
if (line.equals("unsolvable")) {
System.out.println("unsolvable");
continue;
}
String step[] = line.split("");
for (String aStep : step) {
switch (aStep) {
case "u":
map[now] = map[now - 3];
map[now - 3] = "x";
now -= 3;
break;
case "l":
map[now] = map[now - 1];
map[now - 1] = "x";
now -= 1;
break;
case "r":
map[now] = map[now + 1];
map[now + 1] = "x";
now += 1;
break;
case "d":
map[now] = map[now + 3];
map[now + 3] = "x";
now += 3;
break;
}
}
for (String s : map) {
System.out.print(s + " ");//输出结果
}
System.out.println();
}
}