问题: 在8×8的棋盘上分布着n个骑士,他们想约在某一格的中聚会,骑士每天可以像国际象棋中的马那样移动一次,如下图所示,可以从中间向8个方向移动,请你计算n个骑士的最早聚会地点和要走多少天。要求尽早聚会,且n个人走的总步数最少,先到聚会地点的骑士可以不再移动等候其他骑士。 从键盘输入n(0<n<=64),然后依此输入n个骑士的初始位置xi,yi(0<=xi,yi<=7)。屏幕输出以空格分隔的三个整数,分别为聚会点的x,y值,以及要走多少天。
因为水平还是有限,只是按照最原始的方法来解决这个问题。
首先解决数据结构,将每个格子看成一个点。这样共有64个点(xi,yi(0<=xi,yi<=7)),定义每个棋子的位置结构。
struct POS{ int x; int y;
POS *before;
bool Traveled;} ;
首先考虑一个骑士从A点走到B点的问题。计算机解决这个问题只能用遍历,显然得用广度优先。每个节点获取相邻节点可以使用一个[8][2]的数组来描述{+-1,+-2},{+-2,+-1}等当前级的节点遍历完之后,再遍历下一级。B就是搜索的结束条件。把所有的可能步骤都找出来,取出最小的那个就是A到B的最短路径(最少步法)。
考虑到棋盘是对称的,关于坐标轴对称,也关于原点对称,因此很多点经过变换,可以转化成已经求解过的点,比如(2,2)->(3,3)就可以转化成(1,1)->(2,2)的走法,此时就不用再重新遍历,直接查表就可以了。此过程中需要注意的是,在坐标轴上的点的走法与棋盘中间的不同,因为有可能越界。
//×ø±ê±ä»»£¬°üÀ¨Ðýת¡¢Æ½ÒÆvoid AxisTransform(POS &S, POS &D){ //printf("[%d%d] [%d%d]/t", S.x, S.y, D.x, D.y );
if( D.x == S.x ) { swap(D.x,D.y); swap(S.x,S.y); } if( D.y == S.y && S.x > D.x ) { swap(S.x,D.x); }
if( float( D.y - S.y ) / ( D.x - S.x ) < 0 ) { D.x = GRID_X-1 - D.x ; S.x = GRID_X-1 - S.x ;
if( D < S ) { swap(D,S); } }
//Æ½ÒÆ if( !IsAtAxis(S.x, S.y) && !IsAtAxis(D.x, D.y) ) { if( S.y > 1 ) { D.y = abs( D.y - S.y ) + 1 ; S.y = 1 ; } if( S.x > 1 ) { D.x = abs( D.x - S.x ) + 1 ; S.x = 1 ; } } //printf("[%d%d] [%d%d]/n", S.x, S.y, D.x, D.y );};
n个骑士聚会,可能没有什么更好的方法了,只能用穷举。把n个骑士到64个节点所需要的总的步数和天数都算了,进行比较,取出最小的就可以了。