骑士游历问题(C语言代码)
2012-12-14 07:05
190 查看
关于骑士游历问题,大家可以想到的方法是回溯法和贪心算法。回溯法的时间复杂度比较高,贪心算法的时间复杂度就好多了。
骑士游历问题
问题描述:
棋盘大小是8*8,骑士在棋盘任一方格开始游历。要求骑士游历棋盘的每一个方格且每个方格只游历一次。输出骑士的游历路径。
解决思路:
dir0、dir1…dir7由小到大排列,每次选择具有最少出口的方向,假设为dir0。如果骑士走到第n步时,没有出口可以选择,同时,骑士未完全遍历棋盘,那么骑士回溯到上一位置((n-1)步),选择dir1方向。骑士游历依次进行下去。直至走到终点或回到原点。终点代表骑士游历完成,原点代表骑士游历失败(没有可以游历每个方格游历且只游历一次的路径)。这里假设当前位置的八个方向已经按照“具有出口”由小到大排列。
编写程序过程要解决的问题(先考虑简单的问题):
一、每个位置最多有8个方向可以移动分别为:(-2,1) (-1,2) (1,2) (2,1) (2,-1) (1,-2) (-1,-2) (-2,-1);
主函数中骑士在初始位置时,对个变量进行初始化代码:
考虑到解决骑士游历需要用到多个函数。骑士前进、后退函数可能用到这八个方向移动的坐标。故将八个方向移动的坐标定义为全局变量,“int ktmove_x[max_dir],ktmove_y[max_dir];”。
二、判断骑士应该前进还是回溯:骑士在当前位置有可移动的方向则前进,反之,回溯到上一位置。八八棋盘上面的每个方格最多有八个方向可以移动。棋盘数组:“int chessboard[width][width];”,则可以定义一个三维数组来判断方格某一方向是否被访问过:“[b]int is_visted[width][width][max_dir]={0};”,初始化为0,表示64个方格具有的出口方向(最多有八个)未被访问,两个数组同样是全局变量。[/b]
骑士前进函数:
骑士回溯函数:
三、确定骑士是完成游历还是回到原点:暂时使用“1” 与“0”分别表示完成游历与回到原点,无论骑士是完成游历或者回到原点都需要停止骑士的移动。骑士移动过程中,无法确定骑士到底需要前进多少次、回溯多少次。所以使用while()[b]循环。但是,[b]使用“1” 与“0”分别表示完成游历与回到原点,即while()循环进行下去的条件无论是0还是1都不符合。解决办法是将是否完成游历和是否回到原点分成2个功能函数:is_sucess(),1代表完成游历,0代表还未完成游历;int
is_back_to_start(),1代表回到起始原点,0代表没有回到其实原点。那么,while()循环进行下去的条件就是:![b]is_end()&&!is_back_to_start(),既没有完成游历也没有回到原点。[/b][/b][/b]
判定骑士是否回到原点
判定骑士是否到达终点
四、这是最后一个也是最重要的问题:如何使用贪心法选定具有最少路径的方向
选定下一步是否有可以移动的方向,定义函数:select_dir(),定义局部变量count,使用两个for()循环分别计算当前位置周围的八个(最多有八个)位置的可移动方向。找出具有最小可移动方向的出口。
代码中的关键是:变量count赋值给变量min_dir(count=min_dir)。
此外,还有一个函数,判定骑士是否游历成功的函数
到此为止,骑士游历问题结束。
优点:时间复杂度小,花费时间少。空间复杂度相对增加。
缺点:全局变量多,功能函数可移植性比较差。
其他:代码中step自加1和自减1的含义。158-160行为什么step赋值给chessboard[startx][starty]后,就要自加1?若不自加1,当骑士回溯到原点(但是还有可以试探的方向),重新选择可移动的方向时,step等于1,这样,程序就会误判骑士已回溯到原点且无可移动方向。
写代码过程,务必仔细。 我在写select_dir()函数时,不小心将if(is_legal(try_x,try_y)==1&&!is_visted[cur_x][cur_y][i]) 的代码写成:if(is_legal(try_x,try_y)==1&&is_visted[cur_x][cur_y][i]),检查半天也没找到错误。最后,用了近三个小时[b]调试程序才找到这个错误。完整代码我已经上传到我的资源了,下载就行,使用的编程软件是VS2010。[/b]
骑士游历问题
问题描述:
棋盘大小是8*8,骑士在棋盘任一方格开始游历。要求骑士游历棋盘的每一个方格且每个方格只游历一次。输出骑士的游历路径。
解决思路:
dir0、dir1…dir7由小到大排列,每次选择具有最少出口的方向,假设为dir0。如果骑士走到第n步时,没有出口可以选择,同时,骑士未完全遍历棋盘,那么骑士回溯到上一位置((n-1)步),选择dir1方向。骑士游历依次进行下去。直至走到终点或回到原点。终点代表骑士游历完成,原点代表骑士游历失败(没有可以游历每个方格游历且只游历一次的路径)。这里假设当前位置的八个方向已经按照“具有出口”由小到大排列。
编写程序过程要解决的问题(先考虑简单的问题):
一、每个位置最多有8个方向可以移动分别为:(-2,1) (-1,2) (1,2) (2,1) (2,-1) (1,-2) (-1,-2) (-2,-1);
主函数中骑士在初始位置时,对个变量进行初始化代码:
step=1; chessboard[startx][starty]=step; step++; cur_x=startx; cur_y=starty; dir[startx][starty]=max_dir; last_dir=max_dir;
考虑到解决骑士游历需要用到多个函数。骑士前进、后退函数可能用到这八个方向移动的坐标。故将八个方向移动的坐标定义为全局变量,“int ktmove_x[max_dir],ktmove_y[max_dir];”。
二、判断骑士应该前进还是回溯:骑士在当前位置有可移动的方向则前进,反之,回溯到上一位置。八八棋盘上面的每个方格最多有八个方向可以移动。棋盘数组:“int chessboard[width][width];”,则可以定义一个三维数组来判断方格某一方向是否被访问过:“[b]int is_visted[width][width][max_dir]={0};”,初始化为0,表示64个方格具有的出口方向(最多有八个)未被访问,两个数组同样是全局变量。[/b]
骑士前进函数:
void forward() { is_visted[cur_x][cur_y][last_dir]=1;//骑士当前位置下标是last_dir的方向已经试探过了 cur_x+=ktmove_x[last_dir]; cur_y+=ktmove_y[last_dir]; chessboard[cur_x][cur_y]=step;//骑士前进成功,赋值棋盘当前位置的步数给骑士所在的棋盘方格 step++; dir[cur_x][cur_y]=last_dir;//记录当前所在位置前进的方向,骑士需要回溯时使用。 last_dir=max_dir; //初始化last_direction }
骑士回溯函数:
void backward() { int i; step--; chessboard[cur_x][cur_y]=0; last_dir=dir[cur_x][cur_y];//将last_direction重置为上一位置到(curr_x,curr_y)所选择的方向 for(i=0;i<max_dir;i++) //把这个位置的所有可以动的方向重新置为未访问过 is_visted[cur_x][cur_y][i]=0; cur_x-=ktmove_x[last_dir]; cur_y-=ktmove_y[last_dir]; /* 回溯到上一位置,回溯到上一位置之后,在上一位置再试探时,应该从 last_direction+1的方向开始。参看成员函数select_direction()*/ }
三、确定骑士是完成游历还是回到原点:暂时使用“1” 与“0”分别表示完成游历与回到原点,无论骑士是完成游历或者回到原点都需要停止骑士的移动。骑士移动过程中,无法确定骑士到底需要前进多少次、回溯多少次。所以使用while()[b]循环。但是,[b]使用“1” 与“0”分别表示完成游历与回到原点,即while()循环进行下去的条件无论是0还是1都不符合。解决办法是将是否完成游历和是否回到原点分成2个功能函数:is_sucess(),1代表完成游历,0代表还未完成游历;int
is_back_to_start(),1代表回到起始原点,0代表没有回到其实原点。那么,while()循环进行下去的条件就是:![b]is_end()&&!is_back_to_start(),既没有完成游历也没有回到原点。[/b][/b][/b]
判定骑士是否回到原点
int is_back_to_start() { if(step==1) return 1; else return 0; }
判定骑士是否到达终点
int is_end_sucess() { if(step>width*width) return 1; else return 0; }
四、这是最后一个也是最重要的问题:如何使用贪心法选定具有最少路径的方向
选定下一步是否有可以移动的方向,定义函数:select_dir(),定义局部变量count,使用两个for()循环分别计算当前位置周围的八个(最多有八个)位置的可移动方向。找出具有最小可移动方向的出口。
for(i=0;i<max_dir;i++)//对可能的八个方向的出口数目依次进行比较 { try_x=cur_x+ktmove_x[i]; try_y=cur_y+ktmove_y[i]; if(is_legal(try_x,try_y)==1&&!is_visted[cur_x][cur_y][i])//找出可选方向的出口数目 { count=0; for(j=0;j<max_dir;j++)//判断每个出口(最多有八个出口)是否可用 { next_x=try_x+ktmove_x[j]; next_y=try_y+ktmove_y[j]; if(is_legal(next_x,next_y)&&!is_visted[cur_x][cur_y][j]) count++; //出口可用,count自加1 } if(count<min_dir)//类似于比较法判断最小值 { last_dir=i;//将当前i值赋给last_direction,最大可以等于7 min_dir=count;//将count赋值给min_dir,反之,min_dir大小保持不变 } } }
代码中的关键是:变量count赋值给变量min_dir(count=min_dir)。
此外,还有一个函数,判定骑士是否游历成功的函数
int is_tourist_sucess() { do { if(select_dir()) forward(); else backward(); } while(!is_back_to_start()&&!is_end_sucess());//完成游历和回到原点需要分成两个函数 if(is_back_to_start()) return 0;//回到起点,骑士游历失败 else return 1;//到达最后一步,骑士游历成功 }
到此为止,骑士游历问题结束。
优点:时间复杂度小,花费时间少。空间复杂度相对增加。
缺点:全局变量多,功能函数可移植性比较差。
其他:代码中step自加1和自减1的含义。158-160行为什么step赋值给chessboard[startx][starty]后,就要自加1?若不自加1,当骑士回溯到原点(但是还有可以试探的方向),重新选择可移动的方向时,step等于1,这样,程序就会误判骑士已回溯到原点且无可移动方向。
写代码过程,务必仔细。 我在写select_dir()函数时,不小心将if(is_legal(try_x,try_y)==1&&!is_visted[cur_x][cur_y][i]) 的代码写成:if(is_legal(try_x,try_y)==1&&is_visted[cur_x][cur_y][i]),检查半天也没找到错误。最后,用了近三个小时[b]调试程序才找到这个错误。完整代码我已经上传到我的资源了,下载就行,使用的编程软件是VS2010。[/b]
相关文章推荐
- Knight's Tour骑士游历问题(C语言实现)
- 《编程之美》买书问题及c语言代码实现
- 骑士游历问题
- 《编程之美》买书问题及c语言代码实现
- C语言解字符串逆序和单向链表逆序问题的代码示例
- 2011软考软件设计师:C语言代码规范问题(1
- CGI编程 - c语言实现 :网页显示HTML代码的问题
- 回溯算法基础---皇后问题,骑士游历,迷宫求解
- 请大家看一下一个c语言中的链表问题,下面的代码是有错误的!!请大家说出错误的原因,以及修改的方法!!!
- 2011软考软件设计师:C语言代码规范问题(1
- POJ2488 A Knight's Journey(深搜DFS,字典序,骑士游历问题)
- [算法]图算法之骑士遍历问题(象棋中马的遍历问题)分析,C语言实现
- 01背包问题--C语言代码
- 2011软考软件设计师:C语言代码规范问题(1
- gcc编译c语言调用mysql存储过程代码出现的问题list
- 嵌入式系统上C++调用C语言接口代码链接时找不到C函数问题处理
- C语言编程狼追兔子问题代码解析
- 排列问题-c语言代码实战
- C语言解决螺旋矩阵算法问题的代码示例
- 骑士巡游问题源码C语言描述