题目大意:5 * 5的棋盘上摆好了旗子,旗子是按照马走日的规则来的,问能在十步之内将起始的棋盘变成题目所给的棋盘那样吗?可以输出最少步数,不可以就输出不行。
解题思路:这题之前我是想着用bfs,但是那个时候判重的时候没有想到用STL的map,用哈希不太会,直接开数组判重又太大了,用map<string, int>来记录一个棋盘的状态(之前没有想到),后来看了别人的代码,发现可以用迭代dfs,因为这题只需要判断十步内的情况,这样题目所给的时间还算是有余的,因为迭代dfs()比广搜要慢。
思路是:每次限定一个深度来进行深搜,这样就不会死循环,并且这个深度就是要求的步数。每限制一个深度就需要重头开始搜索一遍,这样比较花时间。但是节约了空间。这题的马只能跳到空格上,所以就重空格开始,按照规则构造棋盘,注意每次返回的时候都要返回原来的那个状态。这里代码里还参照了别人的剪枝的思想,提前判断一下现在这个状态要到达目标状态最少还要走多少步,如果现在的步数加上还要走的步数超过限定的深度,就可以直接返回了。还有要注意可以不移动就到达目标状态的情况。
#include<stdio.h>
#include<string.h>
const int M = 5;
int t, deep;
char finish[M][M];
const char init[M][M] = {{'1', '1', '1', '1', '1'},{'0', '1', '1', '1', '1'},{'0', '0', ' ', '1', '1'},{'0', '0', '0', '0', '1'},{'0', '0', '0', '0', '0'}};
const int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
const int dy[] = {-1, -2, -2, -1, 1, 2, 2, 1};
int cut() {
int sum = 0;
for(int i =0; i < M; i++)
for(int j = 0; j < M; j++)
sum += (finish[i][j] != init[i][j]);
return sum / 2;
}
int dfs(int d, int x, int y) {
if(d > deep)
return 0;
if(memcmp(finish, init, sizeof(init)) == 0)
return 1;
if(d + cut() > deep)
return 0;
for(int i = 0; i < 8; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 0 && nx < M && ny >= 0 && ny < M){
int swap;
swap = finish[x][y];
finish[x][y] = finish[nx][ny];
finish[nx][ny] = swap;
if(dfs(d + 1, nx, ny))
return 1;
else{
swap = finish[x][y];
finish[x][y] = finish[nx][ny];
finish[nx][ny] = swap;
}
}
}
return 0;
}
int main(){
scanf("%d%*c", &t);
while(t--){
int i, j, x, y;
for(i = 0; i < M; i++){
for(j = 0; j < M; j++){
scanf("%c", &finish[i][j]);
if(finish[i][j] == ' '){
x = i;
y = j;
}
}
getchar();
}
for(deep = 0; deep < 11; deep++)
if(dfs(0, x, y))
break;
if(deep != 11)
printf("Solvable in %d move(s).\n", deep);
else
printf("Unsolvable in less than 11 move(s).\n");
}
return 0;
}