国际象棋的棋盘为8×8的方格棋盘。现将“马”放在任意指定的方格中,按照“马”走棋的规则将“马”进行移动。要求每个方格只能进入一次,最终使得“马”走遍棋盘的64个方格。
编写一个C程序,实现马踏棋盘操作,要求用1〜64这64个数字标注马移动的路径,也就是按照求出的行走路线,将数字1,2,……64依次填入棋盘的方格中,并输出。
国际象棋中,“马”的移动规则如图1所示。
○ | ○ | |||
○ | ○ | |||
● | ||||
○ | ○ | |||
○ | ○ |
如图1所示,图中实心的圆圈代表“马”的位置,它下一步可移动到图中空心圆圈所标注的8个位置上,该规则叫做“马走日”。但是如果“马”位于棋盘的边界附近,那么它下一步可移动到的位置就不一定有8个了,因为要保证“马”每一步都走在棋盘中。
马踏棋盘的问题其实就是要将1,2,……,64填入到一个8×8的矩阵中,要求相邻的两个数按照“马”的移动规则放置在矩阵中。
例如数字a放置在矩阵的(i,j)位置上,数字a+1只能放置在矩阵的(i-2,j+1),(i-1,j+2),(i+1,j+2),(i+2,j+1),(i+2,j-1),(i+1,j-2),(i-1,j-2),(i-2,j-1)之中的一个位置上。将矩阵填满并输出。
这样在矩阵中从1,2……遍历到64,就得到了马踏棋盘的行走路线。因此本题的最终目的是输出一个8*8的矩阵,在该矩阵中填有1, 2……64这64个数字,相邻数字之间遵照“马走日”的规则。
解决马踏棋盘问题的一种比较容易理解的方法是应用递归的深度优先搜索的思想。因为“马”每走一步都是盲目的,它并不能判断当前的走步一定正确,而只能保证当前这步是可走的。
“马”走的每一步棋都是从它当前位置出发,向下一步的8个位置中的1个行走(在它下一步有8个位置可走的情况下)。因此“马”当前所走的路径并不一定正确,因为它可能还有剩下的可选路径没有尝试马”的行走过程实际上就是一个深度探索的过程。“探索树”的根节点为“马”在棋盘中的初始位置。
接下来“马”有两种行走方式,于是根节点派生出两个分支。而再往下一步行走,根节点的两个孩子又能够分别派生出其他不同的“行走路线”分支,如此派生下去,就得到了 “马”的所有可能的走步状态。
可以想见,该探索树的叶子节点只可能有两种状态:一是该节点不能再派生出其他的“走步”分支了,也就是“马”走不通了;二是棋盘中的每个方格都被走到,即“马”踏遍棋盘。于是从该探索树的根节点到第二种情况的叶节点构成的路径,就是马踏棋盘的行走过程。
如何才能通过搜索这棵探索树,找到这条马踏棋盘的行走路径呢?可以采用深度优先搜索的方法以先序的方式访问树中的各个节点,直到访问到叶节点。
如果叶节点是第二种情况的叶节点,则搜索过程可以结束,因为找到了马踏棋盘的行走路径;如果叶节点为第一种情况的叶节点,即走不通了,则需要返回到上一层的节点,顺着该节点的下一条分支 继续进行深度优先搜索下去。
因此在设计“马踏棋盘”的算法时可以借鉴图的深度优先遍历算法和二叉树的先序遍历算法。但是在这里并不需要真正地构建这样一棵探索树,只需要借用探索树的思想。
在实际的操作过程中,所谓的探索树实际就是深度优先搜索的探索路径,每个节点实际就是当前的棋盘状态,而所谓的叶节点或者是在当前棋盘状态下,“马”无法再进行下一步行走;或者是马踏棋盘成功。
备注:因为程序涉及深度搜索,运行中叶节点不能再派生出其他的“走步”分支时,也就是“马”走不通了,则返回上一节点,比较复杂,运行较慢。(一般需要一分钟左右出现结果,请耐心等待。)
#include <stdio.h>
#define X 8
#define Y 8
int chess[X][Y];
int nextxy(int *x, int *y, int count) /*找到基于x,y位置的下一个可走的位置*/
{
switch(count)
{
case 0:
if(*x+2<=X-1 && *y-1>=0 && chess[*x+2][*y-1]==0)
{
*x=*x+2;
*y=*y-1;
return 1;
}
break;
case 1:
if(*x+2<=X-1 && *y+1<=Y-1 && chess[*x+2][*y+1]==0)
{
*x=*x+2;
*y=*y+1;
return 1;
}
break;
case 2:
if(*x+1<=X-1 && *y-2>=0 && chess[*x+1][*y-2]==0)
{
*x=*x+1;
*y=*y-2;
return 1;
}
break;
case 3:
if(*x+1<=X-1 && *y+2<=Y-1 && chess[*x+1][*y+2]==0)
{
*x=*x+1;
*y=*y+2;
return 1;
}
break;
case 4:
if(*x-2>=0 && *y-1>=0 && chess[*x-2][*y-1]==0)
{
*x=*x-2;
*y=*y-1;
return 1;
}
break;
case 5:
if(*x-2>=0 && *y+1<=Y-1 && chess[*x-2][*y+1]==0)
{
*x=*x-2;
*y=*y+1;
return 1;
}
break;
case 6:
if(*x-1>=0 && *y-2>=0 && chess[*x-1][*y-2]==0)
{
*x=*x-1;
*y=*y-2;
return 1;
}
break;
case 7:
if(*x-1>=0 && *y+2<=Y-1 && chess[*x-1][*y+2]==0)
{
*x=*x-1;
*y=*y+2;
return 1;
}
break;
default:
break;
}
return 0;
}
int TravelChessBoard(int x, int y, int tag) /*深度优先搜索地"马踏棋盘"*/
{
int x1=x, y1=y, flag=0, count=0;
chess[x][y]=tag;
if(tag == X*Y)
{
return 1;
}
flag=nextxy(&x1, &y1, count);
while(flag==0 && count<7)
{
count=count+1;
flag=nextxy(&x1, &y1, count);
}
while(flag)
{
if(TravelChessBoard(x1, y1, tag+1))
return 1;
x1=x;
y1=y;
count=count+1;
flag=nextxy(&x1, &y1, count); /*寻找下一个(x,y)*/
while(flag==0 && count<7)
{ /*循环地寻找下一个(x,y)*/
count=count+1;
flag=nextxy(&x1, &y1, count);
}
}
if(flag == 0)
chess[x][y]=0;
return 0;
}
int main()
{
int i, j;
for(i=0; i<X; i++)
for(j=0; j<Y; j++)
chess[i][j]=0;
if(TravelChessBoard(2, 0, 1))
{
for(i=0; i<X; i++)
{
for(j=0; j<Y; j++)
printf("%-5d", chess[i][j]);
printf("\n");
}
printf("The horse has travelled the chess borad\n");
}
else
printf("The horse cannot travel the chess board\n");
return 0;
}
运行结果: