题目描述
在3*3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局和目标布局,为了使题目简单,设目标状态为:
123
804
765
找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变.
解题报告:
网上已有很多关于八数码的解法,大致有几种方法:深搜,广搜,双向广搜,A*,本解题报告实现了双广的代码,效率略低于A*。
大致思想如下,将起始状态和目的状态都表示成一个字符串,然后设置两个队列,两边分别想中间扩展,当找到相同的节点时,即为找到了一种方案,而广度搜索即找到的是最佳方案,即为题解。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
class Node {
String nowStr;
Node preNode;
char howtom;
int zero;
Node(String nowStr,Node preNode,int zero,char howtom){
this.nowStr = nowStr;
this.preNode = preNode;
this.zero = zero;
this.howtom = howtom;
}
}
public class N06EightShuMa {
static String des = "123804765";
static int[][] move = new int[][]{{-1,0},{0,1},{1,0},{0,-1}};
static char[][] dch = new char[][]{{'u','d'},{'r','l'},{'d','u'},{'l','r'}};
static int firstZero;
static Set<String> firSet = new HashSet<String>();
static Set<String> endSet = new HashSet<String>();
static List<Set> list= new ArrayList<Set>();
static List<Node> firList = new ArrayList<Node>();
static List<Node> endList = new ArrayList<Node>();
static Node stnode;
static Node desnode;
static int[] b = new int[2];
static int[] c =new int[2];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String st = "";
int[] a = new int[9];
for(int i=0;i<9;i++){
a[i] = sc.nextInt();
}
st=IntToStr(a);
stnode = new Node(st,null,firstZero,' ');
firList.add(stnode);
desnode = new Node(des,null,4,' ');
endList.add(desnode);
firSet.add(st);
list.add(firSet);
endSet.add(des);
list.add(endSet);
b[0]=b[1]=1;
while(b[0]!=0&&b[1]!=0){
if(b[0]<b[1]){
if(expand(firList,0)){ //扩展起始队列
return;
}
}else{
if(expand(endList,1)){ //扩展目标队列
return;
}
}
}
while(b[0]!=0){
if(expand(firList,0)){
return;
}
}
while(b[1]!=0){
if(expand(endList,1)){
return;
}
}
System.out.println("不能到达!");
return;
}
private static void prin(String str) { //打印结果
List<String> result = new ArrayList<String>();
Node node =null;
for(int i=0;i<firList.size();i++){
node = firList.get(i);
if(node.nowStr.endsWith(str)){
break;
}
}
while(node.preNode!=null){
result.add(0,node.howtom+"");
node = node.preNode;
}
for(int j=0;j<endList.size();j++){
node = endList.get(j);
if(node.nowStr.endsWith(str)){
break;
}
}
while(node.preNode!=null){
result.add(node.howtom+"");
node = node.preNode;
}
for(String s:result){
System.out.print(s);
}
System.out.println();
System.out.println(result.size());
}
@SuppressWarnings("unchecked")
private static boolean expand(List<Node> que, int k) { //扩展函数
Node node = que.get(c[k]);
int index = node.zero;
b[k]--;
for(int i=0;i<4;i++){
int x = index/3+move[i][0];
int y = index%3+move[i][1];
if(x>=0&&x<=2&&y>=0&&y<=2){
String str = swap(x*3+y,index,node.nowStr);
Node temp = new Node(str,node,x*3+y,dch[i][k]);
if(list.get(1-k).contains(str)){
que.add(temp);
prin(str);
return true;
}
if(!list.get(k).add(str)){
continue;
}
que.add(temp);
b[k]++;
}
}
c[k]++;
return false;
}
private static String swap(int k, int index, String nowStr) { //交换字符位置
String te="";
if(k<index){
return te+nowStr.substring(0, k)+nowStr.charAt(index)+nowStr.substring(k+1,index)+nowStr.charAt(k)+nowStr.substring(index+1);
}else{
return te+nowStr.substring(0, index)+nowStr.charAt(k)+nowStr.substring(index+1,k)+nowStr.charAt(index)+nowStr.substring(k+1);
}
}
private static String IntToStr(int[] a) { //将int数组转换为字符串
String te = "";
for(int i=0;i<9;i++){
te+=a[i];
if(a[i]==0){
firstZero = i;
}
}
return te;
}
}