蓝桥杯 历届试题 九宫重排(双广搜+康拓展开排重)

问题描述

  如下面第一个图的九宫格中,放着 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;
	}
}

 

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值