在一个 3×33×3 的网格中,1∼81∼8 这 88 个数字和一个 x
恰好不重不漏地分布在这 3×33×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
把 x
与上下左右方向数字交换的行动记录为 u
、d
、l
、r
。
现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。
输入格式
输入占一行,将 3×33×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。
如果答案不唯一,输出任意一种合法方案即可。
如果不存在解决方案,则输出 unsolvable
。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
ullddrurdllurdruldr
解题代码
import java.util.*;
class State {
int[] board;
int xIndex;
String moves;
public State(int[] board, int xIndex, String moves) {
this.board = board;
this.xIndex = xIndex;
this.moves = moves;
}
}
public class Main {
static final int[] dx = {0, 0, -1, 1};
static final int[] dy = {-1, 1, 0, 0};
static final char[] dir = {'l', 'r', 'u', 'd'};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] board = new int[9];
int xIndex = 0;
for (int i = 0; i < 9; i++) {
String s = scanner.next();
if (s.equals("x")) {
board[i] = 0;
xIndex = i;
} else {
board[i] = Integer.parseInt(s);
}
}
String result = solve(board, xIndex);
System.out.println(result);
}
public static String solve(int[] board, int xIndex) {
int[] target = {1, 2, 3, 4, 5, 6, 7, 8, 0};
Queue<State> queue = new LinkedList<>();
queue.offer(new State(board, xIndex, ""));
Set<String> visited = new HashSet<>();
while (!queue.isEmpty()) {
State state = queue.poll();
if (Arrays.equals(state.board, target)) {
return state.moves;
}
int x = state.xIndex / 3;
int y = state.xIndex % 3;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx >= 3 || ny < 0 || ny >= 3) continue;
int nIndex = nx * 3 + ny;
int[] newBoard = state.board.clone();
newBoard[state.xIndex] = newBoard[nIndex];
newBoard[nIndex] = 0;
String newMoves = state.moves + dir[i];
String boardStr = Arrays.toString(newBoard);
if (!visited.contains(boardStr)) {
visited.add(boardStr);
queue.offer(new State(newBoard, nIndex, newMoves));
}
}
}
return "unsolvable";
}
}
-
定义状态:
- 我们需要定义一个状态来表示当前的拼图状态。状态包括当前网格的布局、x的位置以及移动步骤。
- 我们可以用一个整数数组来表示网格的布局,其中每个数字表示拼图的一个格子的值,而0表示x。
- x的位置可以用一个整数来表示,例如x在第二行第三列,我们可以将其索引表示为5(行列都从0开始计数)。
- 移动步骤则是一个字符串,记录了从初始状态到当前状态的所有移动。
-
初始化状态:
- 我们从输入中读取初始网格的布局,并找到x的位置。
- 将初始状态加入到BFS队列中。
-
BFS搜索:
- 在每一次BFS迭代中,我们从队列中取出一个状态,并尝试向四个方向移动x,即左、右、上、下。
- 对于每个移动,我们生成一个新的状态,并将其加入到队列中,同时更新移动步骤。
- 如果我们发现某个状态达到了目标状态(即网格布局与目标相同),则返回移动步骤,表示找到了解决方案。
- 如果队列为空,而没有找到目标状态,说明无法达到目标状态,返回"unsolvable"。
-
避免重复状态:
- 为了避免重复搜索相同的状态,我们使用一个集合来记录已经访问过的状态。这样可以确保我们不会陷入无限循环。
-
输出结果:
- 如果找到了解决方案,则返回移动步骤,表示如何从初始状态到达目标状态。
- 如果无法找到解决方案,则返回"unsolvable"。