BZOJ-1085: [SCOI2005]骑士精神

[文章目录]

Description

  在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务。步数>15输出-1

这题好像网上都是IDA*,然而我学了一天的搜索,愣是怀疑人生了。(另外IDA*是个毛???)

由于状态可以表示,并且起末状态已知,我们可以双向广搜,将连终点的一边的改成深搜。连起点一边的迭代加深搜索。然后,期望时间复杂度O(T*3^7)。理论上,感觉两遍bfs也可行,不过主要是不知道队列要开多大,就写成dfs了。另外层数固定,且非常小,不用考虑系统栈的问题。状态用两个int,一个二进制存黑白,另一个存当前空白位置。注意,一定将存黑白的int在now上的二进制赋值成一样的数,不然无法从map里找到该状态。注意:编号与坐标与状态的关系,恩,WA了*3。148ms

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> pr;
pr root;
map<pr,int>v;
int T,ans,lim;
char st[30];
bool flag;
int dx[]={-1,1,-1,1,-2,2,-2,2},dy[]={2,-2,-2,2,1,-1,-1,1};
void output(pr x)//调试
{
	int a=x.first,b=x.second;
	for(int i=0;i<25;i++)
	{
		if(i==b-1) putchar('*');
		else if(a&(1<<i)) putchar('1');
		else putchar('0');
		if(i%5==4) puts("");
	}
	puts("");
}
void initial(pr x,int step,int pre)
{
	if(step>9) return ;
	int now=x.second,xx=(now-1)/5+1,yy=(now-1)%5+1;//各种小BUG
	for(int i=0;i<8;i++)
		if(i!=pre&&xx+dx[i]>0&&xx+dx[i]<=5&&yy+dy[i]>0&&yy+dy[i]<=5)
		{
			int tmp=(xx+dx[i]-1)*5+yy+dy[i];
			if(x.first&(1<<tmp-1))
			{
				x.first=x.first|(1<<now-1);
				x.second=tmp;
				x.first=x.first&(~(1<<x.second-1));//使得关键字相同
				if(!v[x]) v[x]=step;
				else v[x]=min(v[x],step);
				initial(x,step+1,i^1);
				x.first=x.first|(1<<tmp-1);
				x.second=now;
			}
			else
			{
				x.first=x.first&(~(1<<now-1));
				x.second=tmp;
				x.first=x.first&(~(1<<x.second-1));//使得关键字相同
				if(!v[x]) v[x]=step;
				else v[x]=min(v[x],step);
				initial(x,step+1,i^1);
				x.first=x.first&(~(1<<tmp-1));
				x.second=now;
			}
		}
}
void dfs(pr x,int step,int pre)
{
	x.first=x.first&(~(1<<x.second-1));//使得关键字相同
	if(v[x]) {ans=lim+v[x]-1,flag=0; return ;}
	if(step>lim) return ;
	int now=x.second,xx=(now-1)/5+1,yy=(now-1)%5+1;
	for(int i=0;i<8;i++)
		if(i!=pre&&xx+dx[i]>0&&xx+dx[i]<=5&&yy+dy[i]>0&&yy+dy[i]<=5&&flag)
		{
			int tmp=(xx+dx[i]-1)*5+yy+dy[i];
			if(x.first&(1<<tmp-1))
			{
				x.first=x.first|(1<<now-1);
				x.second=tmp;
				dfs(x,step+1,i^1);
				x.first=x.first|(1<<tmp-1);
				x.second=now;
			}
			else
			{
				x.first=x.first&(~(1<<now-1));
				x.second=tmp;
				dfs(x,step+1,i^1);
				x.first=x.first&(~(1<<tmp-1));
				x.second=now;
			}
		}
}
void initialize()//从终点搜索8层
{
	root.second=13; 
	root.first=549855;
	v[root]=1;
	initial(root,2,8);
}
int main()
{
	initialize();
	scanf("%d",&T);
	while(T--)
	{
		pr tmp; tmp.first=0;
		for(int i=1;i<=5;i++)
		{
			scanf("%s",st+1);
			for(int j=1;j<=5;j++)
			{
				if(st[j]=='1') tmp.first|=(1<<(i-1)*5+j-1);
				else if(st[j]=='*') tmp.second=(i-1)*5+j;
			}
		}
		ans=-1; flag=1;
		for(lim=0;flag&&lim<8;lim++) dfs(tmp,1,8);//迭代加深 IDFS 最大 搜7层
		printf("%d\n",ans);
	}
	return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注