这道题说的是有一个象棋棋子马,走日字,问能从左上角开始,走的路径满足字典顺序(字母是字典顺序),问是否能
走完所有的点,有则输出路径,注意,这里有的棋盘可以有很多种走法,但不是字典序列的,所以应该调整好搜索方案
代码如下:
#include <stdio.h>
#include <string.h>
//搜索过程中的路径
int path[100][22];
//搜索的下一跳方法
int dr[8][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
//标记搜索过程中,同一条路径中被标记的位置
int mark[27][27];
//行,列
int p, q;
//总的点数
int total;
//是否搜索成功
int flag;
//打印路径
void print(){
int i,j;
for(i = 1;i <= total;i++){
printf("%c%d", path[i][0] + 64, path[i][1]);
}
printf("\n");
}
//用深度优先搜索查找合适的路径
void dfs(int num){
//当路径经过的点数为总数时,表示遍历完毕
if(num == total){
flag = 1;
print();
return;
}
if(flag){
return ;
}
//保存当前搜索点的位置
int x = path[num][0];
int y = path[num][1];
int i,j;
int xn, yn;
//探询8个不同的方向,尝试是否能实现搜索
for(i = 0;i < 8;i++){
//下一跳位置
xn = x + dr[i][0];
yn = y + dr[i][1];
//满足条件则可以作为路径
if(xn >=1 && xn <= q && yn >=1 && yn <=p && !mark[xn][yn]){
//当前这条路径遍历过了,就不能再遍历了,所以标记为已遍历过
mark[xn][yn] = 1;
//更新下一跳坐标,这里比较重要,不能用num += 1,这样做的错误是,
//因为这是一个循环,如果在这次给更新了num,那么下一次就是下一个点
//了,这就不是在一个点(x,y)来搜索不同的方向,而是下一个点开始搜索了
//所以需要更换变量名
int nnum = num + 1;
path[nnum][0] = xn;
path[nnum][1] = yn;
//进行下一步搜索
dfs(nnum);
//根据递归的思想,在进入dfs()函数到这里调用dfs()函数为止,一直是一条路径
//进行搜索,到这里时,应该走别的路径了,所以应该恢复标记
mark[xn][yn] = 0;
}
}
}
int main(){
int t;
scanf("%d", &t);
int i = 1;
for(i = 1;i <= t;i++){
scanf("%d%d", &p,&q);
total = p * q;
memset(path, 0, sizeof(path));
memset(mark, 0, sizeof(mark));
path[1][0] = 1;
path[1][1] = 1;
mark[1][1] = 1;
flag = 0;
printf("Scenario #%d:\n",i);
dfs(1);
if(!flag){
printf("impossible\n");
}
if(i < t){
printf("\n");
}
}
return 0;
}
关于dfs的理解
准备条件:
1、实现寻找目标步骤的方法,即移动的方法
2、一个可以容纳所有可能节点的容器
3、一个用于存储遍历结果的路径的容器
方法:
1、运用递归函数,逐个遍历,将节点选择的地方放在递归函数内部,
当检测到当前节点可行时,需标记为访问过,并将改节点存入路径中,
然后继续寻找下一个可能的节点,找完后,应将遍历过的节点恢复,
因为有可能走别的路径时,要遍历这个节点。
2、在递归调用的同时,每次在入口测试是否达到目标状态,
如果达到目标状态,则将该路径做一个备份,但不能将其恢复默认值,
因为有的路径当不能到达目标节点时,会回溯到上一次,接着递归遍历,
所以会用到上一次的结果。