题解 UVA1343 【旋转游戏 The Rotation Game】

分析

显然,这题需要深搜

我们先记 AH 的操作分别为 1188,深搜的同时,全局建一个 vector 型变量 ansans 记录答案,对于当前的状态,我们有 88 种移动方式,不断往下搜即可。

一个剪枝

我们的 ansans 种存储了上一次的操作,显然,如果执行上一次的逆操作,那么状态就会回到上一次移动前的状态,造成等效冗余,不能满足最小次数的要求,所以每次不应执行上一次的逆操作。

每个操作的逆操作对应如下:

1-6 2-5 3-8 4-7
5-2 6-1 7-4 8-3

一个优化

显然,如果这题仅仅是这样就不是蓝题了, 我们还需要更多的优化。

可以发现,在每个状态下,如果中间的 88 个格子出现次数最多的数字出现了 xx次,而每次修改只能增加或减少不超过 11 个出现次数最多的数字,那么想要凑满 88 个至少要 8x8-x 次,我们可以使用启发式搜索IDA* ,设估价函数 g()g() 表示当前状态下最少需要的操作次数,从 11 开始从小到大限制搜索的次数,设当前操作次数为 nownow,限制步数为 stepstep,若 now+g()>stepnow+g()>step,则返回不可行。

代码

#include<bits/stdc++.h>
#define mec(a,b) memcpy(a,b,sizeof(a));
using namespace std;
const int N=15;
int a[N][N],step,num[4],dui[9]={0,6,5,8,7,2,1,4,3};
vector<int>ans;
int g()//估价函数
{
	num[1]=num[2]=num[3]=0;
	for(int i=3;i<6;i++)
		for(int j=3;j<6;j++) 
		{
			if(i!=4||j!=4)
				num[a[i][j]]++;//统计出现最多的数的次数
		}
	return 8-max(num[1],max(num[2],num[3]));
}
void move(int op)//移动
{
	switch(op)
	{
		case 1:for(int i=1;i<8;i++)a[i-1][3]=a[i][3];a[7][3]=a[0][3];break;
		case 2:for(int i=1;i<8;i++)a[i-1][5]=a[i][5];a[7][5]=a[0][5];break;
		case 3:for(int i=7;i;i--)a[3][i+1]=a[3][i];a[3][1]=a[3][8];break;
		case 4:for(int i=7;i;i--)a[5][i+1]=a[5][i];a[5][1]=a[5][8];break;
		case 5:for(int i=7;i;i--)a[i+1][5]=a[i][5];a[1][5]=a[8][5];break;
		case 6:for(int i=7;i;i--)a[i+1][3]=a[i][3];a[1][3]=a[8][3];break;
		case 7:for(int i=1;i<8;i++)a[5][i-1]=a[5][i];a[5][7]=a[5][0];break;
		case 8:for(int i=1;i<8;i++)a[3][i-1]=a[3][i];a[3][7]=a[3][0];break;
	}
}
bool dfs(int now)
{
	int gu=g();
	if(!gu)//以成功就返回1
		return 1;
	if(now+gu>step)//超过限制返回0
		return 0;
	int b[N][N];
	mec(b,a);//复制状态
	for(int i=1;i<9;i++)
	{
		if(ans.size()&&ans.back()==dui[i])//不是第一次操作且是逆操作就不执行这个操作
				continue;
		move(i);
		ans.push_back(i);
		if(dfs(now+1))
			return 1;
		ans.pop_back();
		mec(a,b);//改变状态
	}
	return 0;
}
int main()
{
    while(cin>>a[1][3]&&a[1][3])
    {
    	ans.clear();
    	step=0;
    	cin>>a[1][5]>>a[2][3]>>a[2][5];
    	for(int i=1;i<=7;i++)
    		cin>>a[3][i];
    	cin>>a[4][3]>>a[4][5];
    	for(int i=1;i<=7;i++)
    		cin>>a[5][i];
    	cin>>a[6][3]>>a[6][5]>>a[7][3]>>a[7][5];
    	while(!dfs(0))
			step++;
    	if(step)
    	{
    		for(int i=0;i<ans.size();i++)
    			putchar(ans[i]+'A'-1);
    		printf("\n");
		}
		else printf("No moves needed\n");
		printf("%d\n",a[3][3]);
	}
	return 0;
}
posted @ 2021-03-30 17:26  luckydrawbox  阅读(14)  评论(0)    收藏  举报  来源