开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
1 问题描述
马踏棋盘问题也被称作是骑士周游问题。问题描述如下:国际象棋的棋盘为8*8的方格棋盘。现将“马”放在任意指定的方格中, 按照“马”走棋的规则(马走日字)将“马”进行移动。要求每个方格只能进入一次,最终使得“马”走遍棋盘的64个方格。
但当马靠近棋盘边缘时,上述8种位置可能超出棋盘范围,成为不可达的位置。同时由于马的初始位置的不确定性,不能保证马一定能够踏遍棋盘。
问题的输入是马的初始位置,要求马按规则移动,问题的输出是马最终的行走路线顺序。如果没有合适方案,则说明马无法踏遍整个棋盘。
2 问题分析与设计
2.1 问题分析
马踏棋盘问题其实是图的深度优先搜索的一种应用。使用回溯法来解决该问题,假如马儿还没有走遍棋盘上所有方格,但是已经走到了尽头,此时就需要回退查看其他的路径,即在棋盘上不停的回溯。
马踏棋盘问题的解决思路如下:
(1) 需要创建一个二维数组来表示棋盘;
(2) 将当前马儿的位置标记为已访问,然后根据当前位置,计算马还能走哪些位置,并放入到一个集合中,最多有8个位置,每走一步就将步数加一;
(3) 遍历集合中存放的所有位置,查看哪个位置可以走通。若走通就继续,走不通就回溯;
(4) 使用马已走步数与棋盘方格个数比较,判断马是否踏遍整个棋盘,若没有达到应该走的步数,则代表马无法踏遍整个棋盘。
在上述使用回溯法解决马踏棋盘问题时,虽然能够遍历所有情况,但是由于马不同的走法会得到不同的结果,效率也会有所影响。在回溯法中马走的下一步可能不是最优的,这就大大增加了算法时间复杂度,所以考虑使用贪心算法来优化原有算法。具体思路是:在马走下一步之前,对下一步位置集合中所有位置按照该位置下一步选择位置个数进行非递减排序,马儿下一步选择进行排序后的集合中第一个没有走过的位置,减少回溯次数。
2.2 算法流程
对整个问题,采用“回溯算法”与“贪心算法”两种算法来综合解决,算法流程如下:
(1) 使⽤⼀个⼆维数组建⽴整张棋盘,⽤该⼆维数组保存棋盘的每⼀个位置,并标记是否⾛过。
(2) 输入马在棋盘上的起始坐标,将这个位置标记为已⾛过,并将当前步数设为1。
(3) 获得在这个位置上,马下⼀步能⾛的位置集合。
(4) 对下一步位置集合中所有位置按照该位置下一步选择位置个数进行非递减排序。
(5) 遍历集合⾥的所有位置,如果该位置没有访问过,下⼀步就选择它, 并将步数+1,递归访问当前位置的下一步集合。
(6) 设置递归结束的标志。⽤⼀个布尔变量标志马踏棋盘是否成功。当马踏棋盘成功时,步数应该等于棋盘格⼦数。若在某次算法进行中,马走完了所有能走的下⼀步位置,步数还⼩于棋盘格⼦数并且还没成功,说明这个位置不能成功的完成游戏,就把这个位置恢复原样,接下来的递归会重新去寻找合适的路。如果步数等于棋盘总格⼦数,说明马踏棋盘成功,把标志的布尔变量设为true,这样在层层返回时就不会再进⼊上⾯的条件,递归就会逐渐结束⽽不会深⼊下去。
2.3 算法设计
马踏棋盘算法设计如下:
3 运行结果
马踏棋盘问题的实验结果如图所示,马从第1行第一列出发,按照图中数字顺序行走即可踏遍整个棋盘。程序运行共耗时22毫秒,可以看出求解速度还是较快的。