[SCOI2005]骑士精神
题目描述
输入输出格式
输入格式:
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。
输出格式:
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
输入输出样例
输入样例#1:
2
10110
0111
10111
01001
00000
01011
1101
01110
01010
00100
输出样例#1:
7
-1
说明
做完四子连棋再来看这道题目,我想直接用四子连棋的做法直接秒掉,但发现自己还是too young多组数据,而且棋盘增大,原来的方法行不通了,这样写的代价就是这样的:
对,为了测试一下我试了一下这种做法;我提交了一遍,9T1A
这时,有以种方法要出现了那就是 估价函数!!!对于每一张棋盘,当前情况下所需的最少时步数就是此时棋盘上与目标棋盘所不一样的点数,这已经是最少了,只能比它多不可能存在比它少的情况,可以手推一下。
也就是加上这样的一句
if(t + h() > ans ||t + h() > 16)return;
这里是16的原因在于它说的不超过15步包括15 ,所以就要用16,。对这个语句的解释就是,到达当前棋盘状态的步数 + 所需最少步数 如果比当前最优解步数或者超过15步多了,说明这肯定不是最优解,就要进行剪枝。
贴代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 2147483647
using namespace std;
int T;
int sx,sy,ans;
int tx[9] = {0,1,1,2,2,-1,-1,-2,-2};
int ty[9] = {0,2,-2,1,-1,2,-2,1,-1};
char pic[5][5];
int pos[5][5];
char end[5][5] = {{'1','1','1','1','1'},{'0','1','1','1','1'},{'0','0','*','1','1'},{'0','0','0','0','1'},{'0','0','0','0','0'}};
int check(){
for(int i = 0; i <= 4; i++){
for(int j = 0; j<=4; j++){
if(end[i][j] != pic[i][j])return 0;
}
}
return 1;
}
//估价函数
int h(){
int tot = 0;
for(int i = 0; i<=4; i++){
for(int j = 0; j<=4; j++){
if(pic[i][j] != end[i][j])tot++;
}
}
return tot;
}
void dfs(int x,int y,int t){
if(t + h() > ans ||t + h() > 16)return;//判断剪枝
if(check()){
ans = min (ans,t);
return ;
}
for(int i = 1; i<=8; i++){
int dx = x + tx[i];
int dy = y + ty[i];
if(dx <= 4 && dx >= 0 && dy >= 0 && dy <=4 ){
swap(pic[dx][dy],pic[x][y]);
dfs(dx,dy,t + 1);
swap(pic[dx][dy],pic[x][y]);
}
}
return;
}
int main(){
cin >> T;
while(T--){
for(int i = 0; i<=4; i++){
for(int j = 0; j<=4; j++){
cin >> pic[i][j];
if(pic[i][j] == '*')sx = i,sy = j;//记录最初空格位置,也是搜索开始的起点
}
}
ans = inf;
dfs(sx,sy,0);
if(ans == inf)cout << -1 <<'\n';//并没有搜到解
else cout << ans <<'\n';
}
return 0;
}