骑士游历数组任意起点c语言,骑士游历问题(C语言代码)

关于骑士游历问题,大家可以想到的方法是回溯法和贪心算法。回溯法的时间复杂度比较高,贪心算法的时间复杂度就好多了。

骑士游历问题

问题描述:

棋盘大小是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];”,则可以定义一个三维数组来判断方格某一方向是否被访问过:“int is_visted[width][width][max_dir]={0};”,初始化为0,表示64个方格具有的出口方向(最多有八个)未被访问,两个数组同样是全局变量。

骑士前进函数:

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

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()循环。但是,使用“1” 与“0”分别表示完成游历与回到原点,即while()循环进行下去的条件无论是0还是1都不符合。解决办法是将是否完成游历和是否回到原点分成2个功能函数:is_sucess(),1代表完成游历,0代表还未完成游历;int is_back_to_start(),1代表回到起始原点,0代表没有回到其实原点。那么,while()循环进行下去的条件就是:!is_end()&&!is_back_to_start(),既没有完成游历也没有回到原点。

判定骑士是否回到原点

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

{

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

{

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

{

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]),检查半天也没找到错误。最后,用了近三个小时调试程序才找到这个错误。完整代码我已经上传到我的资源了,下载就行,使用的编程软件是VS2010。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值