1. 程式人生 > >ZOJ2477 IDA*搜尋進階

ZOJ2477 IDA*搜尋進階

問題大意就是給你一個魔方,讓你在五步之內還原這個魔方,這個魔方只能讓每個面進行順時針或者逆時針操作,問能否在五步內解決戰鬥,如果可以,請輸出每一步轉動的面,以及是逆時針還是順時針。

分析:對於這個問題,核心在於如何魔方的轉動,以及預估函式的判斷;  關於預估函式,我的觀點是當你想出正確的模擬轉動過程後這個預估函式也就很好寫了,所以先對魔方轉動模擬的分析:首先可以知道每轉動一次都會有20個方塊改變位置,而且對於這道題下的魔方可以有12種移動方法(每個面都可以轉,且每個面都有兩種轉法)於是就大膽的寫出來這12種的魔方方塊移動位次。

同時我們也可以知道一個魔方的還原判斷標準是每一個面的外圍8個方塊與中心相同,於是預估函式也就可以寫出,那就是判斷每個面都有幾個跟中心不相同的方塊,逐面累加,然後把這個數除以12,向上取整就得到在理想情況下的剩餘步數

程式碼:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>

using namespace std;

int n,m,depth;
int ans[10],dir[10];
char a[100];

int centre[6]={5,23,26,29,32,50};
int row[6][10] = { {1, 2, 3, 4, 5, 6, 7, 8, 9},          {10, 11, 12, 22, 23, 24, 34, 35, 36},
					  {13, 14, 15, 25, 26, 27, 37, 38, 39}, {16, 17, 18, 28, 29, 30, 40, 41, 42},
					  {19, 20, 21, 31, 32, 33, 43, 44, 45}, {46, 47, 48, 49, 50, 51, 52, 53, 54} };

int change[12][20]={//12表示12種操作,20是因為每一種操作都會導致20個格子發生變化;
    {12, 24, 36, 35, 34, 22, 10, 11, 13, 25, 37, 46, 49, 52, 45, 33, 21, 1, 4, 7},
	{10, 11, 12, 24, 36, 35, 34, 22, 1, 4, 7, 13, 25, 37, 46, 49, 52, 45, 33, 21},
	{15, 27, 39, 38, 37, 25, 13, 14, 16, 28, 40, 48, 47, 46, 36, 24, 12, 7, 8, 9},
	{13, 14, 15, 27, 39, 38, 37, 25, 7, 8, 9, 16, 28, 40, 48, 47, 46, 36, 24, 12},
	{18, 30, 42, 41, 40, 28, 16, 17, 19, 31, 43, 54, 51, 48, 39, 27, 15, 9, 6, 3},
	{16, 17, 18, 30, 42, 41, 40, 28, 9, 6, 3, 19, 31, 43, 54, 51, 48, 39, 27, 15},
	{21, 33, 45, 44, 43, 31, 19, 20, 3, 2, 1, 10, 22, 34, 52, 53, 54, 42, 30, 18},
	{19, 20, 21, 33, 45, 44, 43, 31, 42, 30, 18, 3, 2, 1, 10, 22, 34, 52, 53, 54},
	{3, 6, 9, 8, 7, 4, 1, 2, 18, 17, 16, 15, 14, 13, 12, 11, 10, 21, 20, 19},
	{1, 2, 3, 6, 9, 8, 7, 4, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10},
	{48, 51, 54, 53, 52, 49, 46, 47, 40, 41, 42, 43, 44, 45, 34, 35, 36, 37, 38, 39},
	{46, 47, 48, 51, 54, 53, 52, 49, 37, 38, 39, 40, 41, 42, 43, 44, 45, 34, 35, 36}
};

int geth()
{
	int sum=0;
	for(int i=0;i<6;i++)
	{
		for(int j=0;j<9;j++)
			if(a[row[i][j]]!=a[centre[i]])	sum++;
	}
	return sum;
}

int solve(int d)
{
	int h=geth();
	if(d+ceil(h/12.0)>depth)	return 0;
	if(h==0)	return 1;
	char maze[100];
	//memcpy(maze,a,sizeof(a));
	for(int i=0;i<12;i++)
	{
		memcpy(maze,a,sizeof(a));
		for(int j=0;j<20;j++)
			a[change[i][j]]=maze[change[i^1][j]];//這裡的^是用來如果i為偶數則+1,若為奇數則i-1,這樣使得在減小程式碼量的前提下進行魔方位置的模擬。
		ans[d]=i/2;
		if(!(i&1))	dir[d]=1;
		else dir[d]=-1;
		if(solve(d+1))
			return 1;
		memcpy(a,maze,sizeof(maze));
	}
	return 0;
}

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		for(int i=1;i<=54;i++)
			cin>>a[i];
		if(!geth())
		{
			printf("0\n");
			continue;
		}
		for(depth=1;depth<=5;depth++)
			if(solve(0))	break;
		if(depth==6)
			printf("-1\n");
		else 
		{
			printf("%d\n",depth);
			for(int i=0;i<depth;i++)
				printf("%d %d\n",ans[i],dir[i]);
		}
	}
	return 0;
}