题目:骑士精神
网址:https://www.luogu.com.cn/problem/P2324
在一个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
输出样例:
7
-1
本题可以使用IDA*求解。
很显然,对于一个状态,对空格像8个方向进行枚举即可。
估价函数怎么运作?
对于如果当前深度 + 该状态下没有归位的棋子个数 > 所限制的深度,剪枝。
不对啊,有反例的:最开始只有一个位置与空格互相错位,并且只需1步即可变成目标状态,那么此时的估价大于实际代价,有误!
因此,估价函数中数量应该是:除空格外,未归位的个数。
这次没有反例吧!
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct rec
{
int x, y;
} st;
const int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
const int dy[8] = {2, 1, -1, -2, 2, 1, -1, -2};
const int dc[5][5] = {{1, 1, 1, 1, 1}, {0, 1, 1, 1, 1}, {0, 0, -1, 1, 1}, {0, 0, 0, 0, 1}, {0, 0, 0, 0, 0}};
int map[5][5];
int calc(int (*mat)[5])
{
int cnt = 0;
if(mat[2][2] != -1) cnt = -1;
for(int i = 0; i < 5; ++ i)
{
for(int j = 0; j < 5; ++ j)
{
if(mat[i][j] != dc[i][j]) ++ cnt;
}
}
return cnt;
}
bool valid(int (*mat)[5])
{
for(int i = 0; i < 5; ++ i)
for(int j = 0; j < 5; ++ j)
if(dc[i][j] != mat[i][j]) return false;
return true;
}
bool dfs(int (*book)[5], rec blank, rec fa, int dep)
{
if(calc(book) > dep) return false;
if(!dep) return valid(book);
int x = blank.x, y = blank.y, mat[5][5];
memcpy(mat, book, sizeof(mat));
rec q;
for(int i = 0; i < 8; ++ i)
{
q = (rec) {x + dx[i], y + dy[i]};
if(q.x < 0 || q.x >= 5 || q.y < 0 || q.y >= 5 || (q.x == fa.x && q.y == fa.y)) continue;
swap(mat[x][y], mat[q.x][q.y]);
if(dfs(mat, q, blank, dep - 1)) return true;
swap(mat[x][y], mat[q.x][q.y]);
}
return false;
}
int main()
{
char c[5][5];
int T;
scanf("%d", &T);
while(T --)
{
for(int i = 0; i < 5; ++ i) cin>> c[i];
for(int i = 0; i < 5; ++ i)
{
for(int j = 0; j < 5; ++ j)
{
switch(c[i][j])
{
case '1':
{
map[i][j] = 1;
break;
}
case '0':
{
map[i][j] = 0;
break;
}
case '*':
{
map[i][j] = -1;
st.x = i, st.y = j;
break;
}
default: break;
}
}
}
bool okay = false;
for(int dep = 0; dep <= 15; ++ dep)
{
if(dfs(map, st, st, dep))
{
okay = true;
printf("%d
", dep);
break;
}
}
if(!okay) puts("-1");
}
return 0;
}