AcWing 回转游戏 dfs IDA* 剪枝 统一操作 java

该文章介绍了一个关于#形棋盘的游戏,棋盘上有数字1,2,3各8个,给定8种不同的操作,目标是通过最少的操作次数使棋盘中间8个格子的数字相同。文章提供了一段Java代码实现,采用IDA*搜索算法寻找解决方案,检查每个操作并递归地尝试所有可能的组合,直到找到满足条件的最小步数。
摘要由CSDN通过智能技术生成

🍑 算法题解专栏


🍑 回转游戏

如下图所示,有一个 # 形的棋盘,上面有 1 , 2 , 3 1,2,3 1,2,3 三种数字各 8 8 8 个。

给定 8 8 8 种操作,分别为图中的 A s i m H A \\sim H AsimH

这些操作会按照图中字母和箭头所指明的方向,把一条长为 7 7 7 的序列循环移动 1 1 1 个单位。

例如下图最左边的 # 形棋盘执行操作 A A A 后,会变为下图中间的 # 形棋盘,再执行操作 C C C 后会变成下图最右边的 # 形棋盘。

给定一个初始状态,请使用最少的操作次数,使 # 形棋盘最中间的 8 8 8 个格子里的数字相同。

在这里插入图片描述

输入格式

输入包含多组测试用例。

每个测试用例占一行,包含 24 24 24 个数字,表示将初始棋盘中的每一个位置的数字,按整体从上到下,同行从左到右的顺序依次列出。

输入样例中的第一个测试用例,对应上图最左边棋盘的初始状态。

当输入只包含一个 0 0 0 的行时,表示输入终止。

输出格式

每个测试用例输出占两行。

第一行包含所有移动步骤,每步移动用大写字母 A s i m H A \\sim H AsimH 中的一个表示,字母之间没有空格,如果不需要移动则输出 No moves needed

第二行包含一个整数,表示移动完成后,中间 8 8 8 个格子里的数字。

如果有多种方案,则输出字典序最小的解决方案。

输入样例:
1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0
输出样例:
AC
2
DDHH
2

在这里插入图片描述

import java.util.Scanner;

/*
      0     1
      2     3
4  5  6  7  8  9 10
      11    12
13 14 15 16 17 18 19
      20    21
      22    23
 */

public class Main
{
	static int N = 24;
	static int[] a = new int[N];
	static int[] path = new int[1000];
//	8个操作对应的 a 数组的下标值
	static int[][] op = { { 0, 2, 6, 11, 15, 20, 22 }, { 1, 3, 8, 12, 17, 21, 23 }, { 10, 9, 8, 7, 6, 5, 4 },
			{ 19, 18, 17, 16, 15, 14, 13 }, { 23, 21, 17, 12, 8, 3, 1 }, { 22, 20, 15, 11, 6, 2, 0 },
			{ 13, 14, 15, 16, 17, 18, 19 }, { 4, 5, 6, 7, 8, 9, 10 } };

//	中心的八个数对应的下标值
	static int[] center = { 6, 7, 8, 11, 12, 15, 16, 17 };
	static int[] opposite = { 5, 4, 7, 6, 1, 0, 3, 2 };// 记录操作 i 的相反操作

	static boolean check()
	{
		for (int i = 0; i < 7; i++)
			if (a[center[i]] != a[center[i + 1]])
				return false;
		return true;
	}

//	估价函数:每次移动顶多增加一个相同值,8 - 最大重复值个数 = 最优预估步数
	static int f()
	{
		int[] cnt = new int[4];
		for (int i = 0; i < 8; i++)
			cnt[a[center[i]]]++;
		int s = 0;
		for (int i = 1; i <= 3; i++)
			s = Math.max(s, cnt[i]);
		return 8 - s;
	}

//	操作函数:将所有元素往头元素的方向移一位,头元素放在最后一位
	static void operation(int x)
	{
		int t = a[op[x][0]];// 存头元素
		for (int i = 0; i < 6; i++)
			a[op[x][i]] = a[op[x][i + 1]];
		a[op[x][6]] = t;
	}
//	u 表示当前层数,maxd 表示IDA*的最大深度,pre表示前一个操作(避免做无用功)
	static boolean dfs(int u, int maxd, int pre)
	{
//		IDA*加深
		if (u + f() > maxd)
			return false;
//		递归出口
		if (check())
			return true;

//		枚举操作
		for (int i = 0; i < 8; i++)
		{
			if (opposite[i] == pre)// 做相反的操作 == 白做
				continue;
			operation(i);// 进行 i 操作
			path[u] = i;
			if (dfs(u + 1, maxd, i))
				return true;
			operation(opposite[i]);
		}
		return false;
	}

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		while (sc.hasNext())
		{
			int a1 = sc.nextInt();
			if (a1 == 0)
				break;
			a[0] = a1;
			for (int i = 1; i < 24; i++)
				a[i] = sc.nextInt();
			int dep = 0;
			while (!dfs(0, dep, -1))
			{
				dep++;
			}
			if (dep == 0)
				System.out.println("No moves needed");
			else
			{
				for (int i = 0; i < dep; i++)
					System.out.printf("%c", 'A' + path[i]);
				System.out.println();
			}
				System.out.println(a[6]);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值