版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/88913997
题目描述
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑 士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空 位上。 给定一个初始的棋盘,怎样才能经过移动变成如下,目标棋盘: 为了体现出骑士精神,他们必须以最少的步 数完成任务。
Solution
这题在每一次的跳跃中,必然与空格有关,所以我们可以这么理解这个问题:问空格和这些马最少经过几次交换达到最终状态,所以我们需要考虑的点是空格而并非马。
由于答案不超过 步,可以使用迭代加深搜索来解决。我们可以得到一个思路就是:枚举每一层的深度,然后再以空白格进行不断的交换,暴力dfs即可。
此时时间复杂度一定很高,我们需要利用一个估价函数来进行优化。估价函数就是当前最优解或是理想情况中的一个答案,估价函数一定优于或等于实际的答案;例如本题中,对于一个地图,所对应的估价函数就是当前这一个地图变成最后地图的最少步数且这一个步数一定≤实际步数。
那么我们就可以利用估价函数来进行剪枝:如果当前步数+估价步数>当前的最大深度,退出搜索。
至于估价函数的书写,我们可以这样判断:如果当前节点与最终节点不相同,估价值 。假设这一个值为 ,则估价函数的具体值 ,则当前深度 限制深度则答案一定不存在,退出即可。但是如何保证一定小于等于呢?
- 个点错位,最少且最优的交换次数就是交换一个轮回就交换成功。例如 的马发生错位现象,则 交换 , 交换 ,…, 交换 。这是等于的情况。
- 很有可能不存在上述情况,例如两个错位的点不存在空格则无法产生移动,我们要利用空格来进行移动显然答案不知一次。
这样的,我们把迭代加深DFS+估价函数的算法成为IDA*算法。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int sx,sy;
int a[6][6];
int last[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,-2,-2,-1,1,2,2,1};
int dy[8]={-2,-1,1,2,2,1,-1,-2};
int func(void)
{
int sum=0;
for (int i=1;i<=5;++i)
for (int j=1;j<=5;++j)
if (a[i][j]^last[i][j])
sum ++;
return sum;
}
bool in (int x,int y)
{
return x>=1 && x<=5 && y>=1 && y<=5;
}
bool dfs(int x,int y,int deep,int MAX)
{
if (deep==MAX)
{
if (!func()) return 1;
return 0;
}
for (int i=0;i<8;++i)
{
int nx=x+dx[i];
int ny=y+dy[i];
if (!in(nx,ny)) continue;
swap(a[x][y],a[nx][ny]);
if (func()-1+deep<=MAX)
if (dfs(nx,ny,deep+1,MAX)) return 1;
swap(a[x][y],a[nx][ny]);
}
return 0;
}
int main(void)
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
int T;
cin>>T;
int cnt=0;
again:
for (int i=1;i<=5;++i)
for (int j=1;j<=5;++j)
{
char c;
cin>>c;
if (c=='*') a[i][j]=2,sx=i,sy=j;
else a[i][j]=c-48;
}
bool flag=0;
for (int i=0;i<=15;++i)
{
if (dfs(sx,sy,0,i))
{
cout<<i<<endl;
flag=1;
break;
}
}
if (!flag) cout<<"-1\n";
if (++cnt<T) goto again;
return 0;
}