您的位置:首页 > 其它

骑士游历算法

2016-03-16 15:26 204 查看
1 骑士游历问题

  在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次。若给定起始位置(x0,y0),编程探索出一条路径,沿着这条路径马能遍历棋盘上的所有单元格。

  设当前马在棋盘的某个位置(x,y)上,按照规则,下一步有8个方向可走。

  设二维数组mat表示棋盘,每个元素表示棋盘的一格,其值定义如下:

  0表示马未到达过

  Mat[i,j]= -1表示棋盘边界

  自然数 表示马到达该格的步数

2 常规的回溯算法

  2.1 设计思想

  马从棋盘上的某一初始位置(x0,y0)开始,每次选择一个方向k,向前走一格,直到走完64格为止。每走一格,设置数组中相应格的元素值为马走的步数。

  如果选择的方向k=0,表示可能的8种走向都试探不通,不通的原因是走向超出了棋盘范围,或当前位置已经被马访问过。此时马已无路可走,说明前几步走得不对,应该退回去重新换一种走法,这种逐步试探的设计思想称为回溯算法。

  2.2 性能评价

  回溯算法在每一格上朝一个方向盲目地进行试探,遇到在某一格上所有方向都不能走通时,才回到前一格上来试探另一个方向。当每一格上的所有方向都试探过,不能走通时,才做出“走不通”的结论。因此该算法在探索时带有一定的盲目性和随机性,运行效率较低。

3 预见算法

  3.1 设计思想

  回溯算法的思路是可行的,但它的运行效率较低,原因在于每步试探的随机性和盲目性。如果能够找到一种克服这种随机性和盲目性的办法,按照一定规律选择前进的方向,则将增加成功的可能性,运行时间也大为缩短。本文提出的算法在这方面有所突破。

  如果在每步选择方向时,不是任意选择一个方向,而是经过一定的测试和计算,“预见”每条路的“宽窄”,再选择一条最“窄”的路先走,成功的可能性较大。理由是先走“困难的路”,光明大道留在后面。因为每一格迟早都要走到,与其把困难留在后面,不如先走“困难的路”,这样路就会越走越宽,成功的机会就越大。这种方法称为预见算法。

  3.2 实现手段

  为每个方向测定一个值――可通路数,它表示该位置上还有多少条通路。在每一格上对8个方向都进行试探,并分析比较,下一步应该选择可通路数值最小的方向走。

下面表格是有多少个位置可以到达当前这个位置:

23444432
34666643
46888864
46888864
46888864
46888864
34666643
23444432
下面上代码

public class Knight {

private boolean Travel(int firstX, int firstY, int[][] board) {

// 对应骑士可走的8个方向
int[] movex = { -2, -1, 1, 2,  2,  1, -1, -2 };
int[] movey = {  1,  2, 2, 1, -1, -2, -2, -1 };

// 下一步出路的位置
int[] nextStepX = new int[board.length];
int[] nextStepY = new int[board.length];

// 记录出路的个数
int[] exitS = new int[board.length];
int nextX = firstX;
int nextY = firstY;
board[nextX][nextY] = 1;

for (int m = 2; m <= Math.pow(board.length, 2); m++) {

//初始化下一个位置可走的位置的数目
for (int i = 0; i < board.length; i++) {
exitS[i] = 0;
}

int count = 0;
// 试探8个方向
for (int i = 0; i < 8; i++) {
int temI = nextX + movex[i];
int temJ = nextY + movey[i];

// 走到边界,路断
if (temI < 0 || temI > 7 || temJ < 0 || temJ > 7) {
continue;
}

// 记录下可走的方向
if (0 == board[temI][temJ]) {
nextStepX[count] = temI;
nextStepY[count] = temJ;
count++;
}
}

// 到这里,cout表示当前点有几种走法。nextStep中存储各种走法的坐标。
int min = -1;
if (count == 0) {
return false;
}

if (1 == count) {
min = 0;
} else {// 这一步是为了找到下一次走法中最少种走法的那一步
for (int i = 0; i < count; i++) {
for (int j = 0; j < 8; j++) {
int temI = nextStepX[i] + movex[j];
int temJ = nextStepY[i] + movey[j];
if (temI < 0 || temI > 7 || temJ < 0 || temJ > 7) {
continue;
}

// 记录下这个位置可走的方向数
if (0 == board[temI][temJ]) {
exitS[i]++;
}
}
}

int tem = exitS[0];
min = 0;

// 从可走的方向中,寻找最少走的出路
for (int i = 1; i < count; i++) {
if (tem > exitS[i]) {
tem = exitS[i];
min = i;
}
}
}

// 得到最少的出路
nextX = nextStepX[min];
nextY = nextStepY[min];
board[nextX][nextY] = m;
}

return true;
}

public static void main(String[] args) {

int firstX, firstY;
System.out.println("输入起始点(0-7):");
Scanner scanner = new Scanner(System.in);

firstX = scanner.nextInt();
firstY = scanner.nextInt();
int[][] board = new int[8][8];
Knight knight = new Knight();

if (knight.Travel(firstX, firstY, board)) {
System.out.println("游历完成:");
} else {
System.out.println("游历失败!\n");
}

for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] < 10) {
System.out.print(" " + board[i][j]);
} else {
System.out.print(board[i][j]);
}
System.out.print(" ");
}
System.out.println();
}
}
}


参看文献:
http://www.xzbu.com/8/view-1086965.htm http://www.wuji8.com/meta/266880944.html http://wenku.baidu.com/link?url=kDUHpgB1ZPtk_CDE8l0q_ekArNkRiOPMHBb-YDcNsupACwajBJt2jm3ySyR2D6b7llZdVhnXVRiGhmdfP7-uNUQ7MuaUO3lFZUhFJ4TkE4C
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: