题目描述
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士,且有一个空位。
在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。
给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:为了体现出骑士精神,他们必须以最少的步数完成任务。
输入格式
第一行有一个正整数T(T<=10),表示一共有T组数据。
接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。
两组数据之间没有空行。
输出格式
每组数据输出占一行。
如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
样例
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
题目分析
题意:给你一个5x5的棋盘,按照“马走日”的方式移动棋子,用最少的步骤把输入的棋盘恢复成原样
解法: IDA*
可以发现,每次枚举棋子的移动很难搜索
通过观察,因为每次马都只能移动到空格上面,那么问题就可以等价成移动空格使棋盘恢复原样
接下来设计估价函数f()
每次移动最多能让一个棋子归位
所以估价函数设计为:当前局面(除了空格)还有多少个棋子未归位
接下来就是常规的IDA*做法了
时间复杂度 O(8^15)
15步有解,每步拓展八个方向
因为会及时剪枝,时间复杂度远小于8^15
C++ 代码
#include<bits/stdc++.h>
using namespace std;
//黑-1白1k空0
int n;
int b[5][5]= {-1,-1,-1,-1,-1,
1,-1,-1,-1,-1,
1,1,0,-1,-1,
1,1,1,1,-1,
1,1,1,1,1
};
int a[5][5];
int dx[]={1,-1,-1,1,2,-2,-2,2},dy[]={2,-2,2,-2,1,-1,1,-1};
int f()
{
int res=0;
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
if(a[i][j]&&a[i][j]!=b[i][j])
res++;
return res;
}
bool dfs(int d,int x,int y,int depth)
{
int val=f();
if(!val){
return true;
}
if(d+val>depth)
return false;
for(int i=0;i<8;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(tx<0||tx>=5||ty<0||ty>=5)continue;
swap(a[x][y],a[tx][ty]);
if(dfs(d+1,tx,ty,depth))
return true;
swap(a[x][y],a[tx][ty]);
}
return false;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int x,y;
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
{
char c;
cin>>c;
if(c=='0')
a[i][j]=1;
else if(c=='1')
a[i][j]=-1;
else
a[i][j]=0,x=i,y=j;
}
bool flag=1;
for(int depth=0;depth<=15; depth++)
{
if(dfs(0,x,y,depth))
{
cout<<depth<<endl;
flag=0;
break;
}
}
if(flag)
cout<<-1<<endl;
}
return 0;
}
dalao写得很好,但可以记录先前的路径,避免走无效dfs
(除了这一部分,其他基本照抄)
$tql$
不用记录会不会走到重复的状态?
IDA*不能记录这个
66666
tql%%%
%%%
666大佬
Orz太强了