AcWing 845. 八数码
【题目描述】
【思路】
将问题抽象,网格的所有状态看成图的一个节点,如果一个状态可以通过一步变换到另一个状态则这两个节点是联通的,且边的权值是1
一些小细节
这题折腾了许久,输入上有些细节,输入数据是多个空格的。
reader.nextLine().split(” “); //如果split传入一个空格,分割结果是[2, , 3, , 4, , 1, , 5, , x, , 7, , 6, , 8] 长度是17
传入两个空格,分割结果是:[2 3 4 1 5 x 7 6 8] 长度为1。可以使用reader.nextLine().split(“\s+”)得到 想要的结果。因为在正则表达式中\s表示空格 \s防止转义 +表示匹配一次或多次。
难点在于状态的表示和记录每个状态的距离
正解
import java.util.*;
public class Main{
public static void swap(char c[], int k, int t){
char g = c[k];
c[k] = c[t];
c[t] = g;
}
public static int bfs(String start){
Queue<String> q = new LinkedList<String>(); //状态队列
Map<String, Integer> d = new HashMap<String, Integer>(); //保存每个状态对应的步数
String end = "12345678x"; //目标状态
q.offer(start);
d.put(start, 0);
int dir[][] = {{-1,0}, {0, 1}, {1, 0}, {0, - 1}};
while(!q.isEmpty() ){
String state = q.poll();
int distance = d.get(state);
if( state.equals(end) ) return distance;//到达目标状态
int k = state.indexOf('x'); //x在字符串的位置
int x = k / 3, y = k % 3; //对应矩阵位置
for(int i = 0; i < 4; i ++){
int a = x + dir[i][0], b = y +dir[i][1];
if( a >= 0 && a < 3 && b >= 0 && b < 3){
char c[] = state.toCharArray();
swap(c, k, a * 3 + b);
String s = new String(c);
//移动之后
if( !d.containsKey(s)){
d.put(s, distance + 1);
q.offer(s);
}
}
}
}
return - 1;
}
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
String start ="";
while(reader.hasNext()) start+= reader.next();
System.out.println( bfs(start) );
}
}
这样记录不知道为啥 答案不对 以后再回来看看
/*
每走一步都是不同排列 需要用st标记数组单独存储每一条路径
*/
import java.util.*;
class Node{
int x, y,step;
char [][] m = new char[3][3];
boolean st[][] = new boolean[3][3];
public Node(int xx, int yy, int tt, char[][] c, boolean[][] vis){
x = xx;
y = yy;
step = tt;
for(int i = 0; i < 3; i ++)
for(int j = 0; j < 3; j ++){
this.m[i][j] = c[i][j];
this.st[i][j] = vis[i][j];
}
}
}
public class Main{
static int N = 3;
static char [][] g = new char[N][N];
static char [][] target = {{'1','2','3'},{'4','5','6'},{'7','8','x'}};
public static boolean check(char c[][]){
for(int i = 0; i < N; i ++ )
for(int j = 0; j < N; j++)
if( c[i][j] != target[i][j]) return false;
return true;
}
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
//2 3 4 1 5 x 7 6 8
//一个空格:[2, , 3, , 4, , 1, , 5, , x, , 7, , 6, , 8] 17
//两个空格:[2 3 4 1 5 x 7 6 8] 1
//正则表达式中\s表示空格 \\s防止转义 +表示匹配一次或多次
String s[] = reader.nextLine().split("\\s+");
int sx =0, sy = 0;
for(int i = 0; i < N; i ++)
for(int j = 0; j < N; j++){
g[i][j] = s[ i * 3 + j].charAt(0);
if(g[i][j] == 'x'){
sx = i;
sy = j;
}
}
if(check(g)) System.out.println(0);
else{
int dir[][] = {{-1,0},{0,1},{1,0},{0,-1}};
boolean st[][] = new boolean[N][N];
st[sx][sy] = true;
Node first = new Node(sx, sy, 0, g,st);
Queue<Node> q = new LinkedList<Node>();
q.offer( first);
while( ! q.isEmpty()){
Node node = q.poll();
int x = node.x, y = node.y, t = node.step;
for(int i = 0; i < 4; i ++){
int a = x +dir[i][0], b = y +dir[i][1];
if( a < 0 || a >=N || b < 0 || b>= N) continue;
if( node.st[a][b]) continue;
Node next = new Node(a, b, t + 1, node.m, node.st);
//交换 (x,y)位置和(a,b)
char tmp = next.m[x][y];
next.m[x][y] = next.m[a][b];
next.m[a][b] = tmp;
next.st[a][b] = true;
if(check(next.m)){
System.out.println(next.step);
return;
}
q.offer(next);
}
}
System.out.println(- 1);
}
}
}