题目:10422 - Knights in FEN
题目大意: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; }