Description
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务。
Sample Input
2
10110
01*11
01001
01011
01110
00100
Sample Output
7
-1
可以将空格看成骑士,进行dfs,让它每次与黑或白马交换。
但是时间其实是很多的,我连数据都过不了……
于是我就学习到了A*算法,这个算法主要是评估一个值为当前到目标状态的大概步数,这个评估步数大于实际步数。
若我来到一个状态,可进行判断,是否需要删除,这样可以省略很多无用状态。
但还有一些小优化,因为步数较小,就可以从小到大枚举每一种步数,每次枚举中dfs只判断是否与该步数相等,这样可以省一些时间,也只有这样才可以在我家电脑上过……
大视野不用这个优化都可以过。
爆搜的做法会超时,你想要直接把跟A_star有关的删掉。
#include<cstdio>
#include<algorithm>
using namespace std;
const int ed[6][6]={
{0,0,0,0,0,0},
{0,1,1,1,1,1},
{0,0,1,1,1,1},
{0,0,0,2,1,1},
{0,0,0,0,0,1},
{0,0,0,0,0,0}
};
int dx[8]={1,1,-1,-1,2,2,-2,-2};
int dy[8]={2,-2,2,-2,1,-1,1,-1};
int v[6][6],ans;
bool A_star(int s)
{
int k=0;
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
if(v[i][j]!=ed[i][j])
{
k++;
if(k+s>ans)return 0;
}
}
}
return 1;
}
bool ok()
{
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
if(v[i][j]!=ed[i][j])return 0;
}
}
return 1;
}
bool bk;
void dfs(int x,int y,int s)
{
if(s>ans)return;
if(ok())
{
if(s==ans)bk=false;
return;
}
for(int i=0;i<8;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(nx<=5&&nx>0&&ny<=5&&ny>0)
{
swap(v[x][y],v[nx][ny]);
if(A_star(s))dfs(nx,ny,s+1);
swap(v[x][y],v[nx][ny]);
}
}
}
char ss[10];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int sx,sy;
for(int i=1;i<=5;i++)
{
scanf("%s",ss+1);
for(int j=1;j<=5;j++)
{
if(ss[j]=='*')v[i][j]=2,sx=i,sy=j;
else v[i][j]=ss[j]-'0';
}
}
bk=true;
for(ans=0;ans<=15;ans++)
{
dfs(sx,sy,0);
if(!bk){printf("%d\n",ans);break;}
}
if(bk)printf("-1\n");
}
return 0;
}