🍑 算法题解专栏
🍑 回转游戏
如下图所示,有一个 # 形的棋盘,上面有  
     
      
       
       
         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]);
		}
	}
}



















