问题描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
样例输入
13524678.
46758123.
样例输出
22
// 双广搜(起始位置分别为始态和终态)+康拓展开排重
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
// 被访问过的为始态到该状态所需要的步数,未被访问过的为-1
static int[] state1 = new int[362880];
// 被访问过的为终态到该状态所需要的步数,未被访问过的为-1
static int[] state2 = new int[362880];
// 四行,分别表示空白格向上、下、左、右四个方向走
static int[][] dir = { { 1, 0 }, { -1, 0 }, { 0, -1 }, { 0, 1 } };
public static void main(String[] args) {
Arrays.fill(state1, -1);
Arrays.fill(state2, -1);
Scanner sc = new Scanner(System.in);
String start = sc.nextLine();
String end = sc.nextLine();
int ans = bfs(start, end);
System.out.println(ans);
sc.close();
}
public static int bfs(String start, String end) {
LinkedList<Run> list1 = new LinkedList<Run>();
LinkedList<Run> list2 = new LinkedList<Run>();
list1.add(new Run(start, 0));
list2.add(new Run(end, 0));
while (!list1.isEmpty()) {
Run head1 = list1.get(0);
list1.remove(0);
Run head2 = list2.get(0);
list2.remove(0);
int step1 = head1.step;
if (head1.str.equals(end)) {
return step1;
}
int step2 = head2.step;
if (head2.str.equals(start)) {
return step2;
}
char[][] ch1 = stringToArr(head1.str);
int pointIndex1 = head1.str.indexOf('.');
int pointX1 = pointIndex1 / 3;
int pointY1 = pointIndex1 % 3;
char[][] ch2 = stringToArr(head2.str);
int pointIndex2 = head2.str.indexOf('.');
int pointX2 = pointIndex2 / 3;
int pointY2 = pointIndex2 % 3;
for (int i = 0; i < dir.length; i++) {
int nextX1 = pointX1 + dir[i][0];
int nextY1 = pointY1 + dir[i][1];
int nextX2 = pointX2 + dir[i][0];
int nextY2 = pointY2 + dir[i][1];
if (nextX1 >= 0 && nextX1 <= 2 && nextY1 >= 0 && nextY1 <= 2) {
{
char temp = ch1[pointX1][pointY1];
ch1[pointX1][pointY1] = ch1[nextX1][nextY1];
ch1[nextX1][nextY1] = temp;
}
String s = arrToString(ch1);
if (state1[kangtuo(s)] == -1) {
list1.addLast(new Run(s, step1 + 1));
state1[kangtuo(s)] = step1 + 1;
}
if (state2[kangtuo(s)] != -1) {
return step1 + 1 + state2[kangtuo(s)];
}
{
char temp = ch1[pointX1][pointY1];
ch1[pointX1][pointY1] = ch1[nextX1][nextY1];
ch1[nextX1][nextY1] = temp;
}
}
if (nextX2 >= 0 && nextX2 <= 2 && nextY2 >= 0 && nextY2 <= 2) {
{
char temp = ch2[pointX2][pointY2];
ch2[pointX2][pointY2] = ch2[nextX2][nextY2];
ch2[nextX2][nextY2] = temp;
}
String s = arrToString(ch2);
if (state2[kangtuo(s)] == -1) {
list2.addLast(new Run(s, step2 + 1));
state2[kangtuo(s)] = step2 + 1;
}
if (state1[kangtuo(s)] != -1) {
return step2 + 1 + state2[kangtuo(s)];
}
{
char temp = ch2[pointX2][pointY2];
ch2[pointX2][pointY2] = ch2[nextX2][nextY2];
ch2[nextX2][nextY2] = temp;
}
}
}
}
return -1;
}
// 康拓展开判重
public static int kangtuo(String str) {
int len = str.length();
// 0~8的阶乘
int[] fac = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
// 存放由康拓展开计算出来的序数
int sum = 0;
int num = 0;
for (int i = 0; i < len; i++) {
num = 0;
for (int j = i + 1; j < len; j++) {
if (str.charAt(j) - '0' < str.charAt(i) - '0')
num++;
}
sum += num * fac[len - 1 - i];
}
return sum;
}
// 将字符串转为二维数组
public static char[][] stringToArr(String str) {
char[][] a = new char[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
a[i][j] = str.charAt(i * 3 + j);
}
}
return a;
}
// 将二维数组转为字符串
public static String arrToString(char[][] a) {
String s = "";
for (int i = 0; i < a.length; i++) {
s += new String(a[i]);
}
return s;
}
}
// 他的实例存放着两个属性,一个String表示当前状态,一个step表示走到这个状态需要几步
class Run {
String str;
int step;
public Run(String str, int step) {
this.str = str;
this.step = step;
}
}