每日一算法:骑士遍历问题

举报
悦来客栈的老板 发表于 2020/12/30 00:45:57 2020/12/30
【摘要】 说明骑士旅游(Knight tour)在十八世纪初倍受数学家与拼图迷的注意,它什么时候被提出已不可考,骑士的走法为西洋棋的走法,骑士可以由任一个位置出发,它要如何走完[所有的位置? 解法骑士的走法,基本上可以使用递回来解决,但是纯綷的递回在维度大时相当没有效率,一个聪明的解法由J.C. Warnsdorff在1823年提出,简单的说,先将最难的位置走完,接下来的路就...

说明骑士旅游(Knight tour)在十八世纪初倍受数学家与拼图迷的注意,它什么时候被提出已不可考,骑士的走法为西洋棋的走法,骑士可以由任一个位置出发,它要如何走完[所有的位置?

解法骑士的走法,基本上可以使用递回来解决,但是纯綷的递回在维度大时相当没有效率,一个聪明的解法由J.C. Warnsdorff1823年提出,简单的说,先将最难的位置走完,接下来的路就宽广了,骑士所要走的下一步,「为下一步再选择时,所能走的步数最少的一步。」,使用这个方法,在不使用递回的情况下,可以有较高的机率找出走法(找不到走法的机会也是有的)。


显然求解骑士游历问题的每一步就是马在棋盘上走的一步。在每一步马需要选择一个方向进行游历,这时记住解的每一步需要记住两件事:
1.当前步的行列位置
2.当前步已经试探过哪些方向了,以便回溯回来时能够选择一个新的方向进行试探

对于骑士游历问题一个启发式规则是,在选择当前步的方向时去选择满足下面条件的方向,
当按这个方向推进到下一位置时,这个位置所可以再选择的方向最少。
也就是说在当前位置优先选一个走起来比"艰难"的方向来推进。加入这种启发式规则之后,从运行的效果看,在求解的过程中几乎不回溯。

 


  
  1. #include <stdio.h>
  2. int travel(int x, int y);
  3. int board[8][8] = {0};
  4. int main(void)
  5. {
  6. int startx, starty;
  7. int i, j;
  8. printf("输入起始点:");
  9. scanf("%d %d", &startx, &starty);
  10. if(travel(startx, starty))
  11. {
  12. printf("游历完成!\n");
  13. for(i = 0; i < 8; i++)
  14. {
  15. for(j = 0; j < 8; j++)
  16. {
  17. printf("%2d ", board[i][j]);
  18. }
  19. printf("\n");
  20. }
  21. }
  22. else
  23. {
  24. printf("游历失败!\n");
  25. }
  26. return 0;
  27. }
  28. int travel(int x, int y)
  29. {
  30. // 对应骑士可走的八个方向 ,按顺时针方向查找
  31. int ktmove1[8] = {-2, -1, 1, 2, 2, 1, -1, -2};//左右方向
  32. int ktmove2[8] = {1, 2, 2, 1, -1, -2, -2, -1};//上下方向
  33. // 测试下一步的出路
  34. int nexti[8] = {0};//左右方向
  35. int nextj[8] = {0};//上下方向
  36. // 记录出路的个数,最少的个数即为我们所求
  37. int exists[8] = {0};
  38. int i, j, k, m, l;
  39. int tmpi, tmpj;
  40. int count, min, tmp;
  41. i = x;
  42. j = y;
  43. board[i][j] = 1;//设置入口处为1
  44. for(m = 2; m <= 64; m++)
  45. {//2...64
  46. for(l = 0; l < 8; l++)
  47. {//出路的个数初始为0
  48. exists[l] = 0;
  49. }
  50. l = 0;
  51. // 试探八个方向
  52. for(k = 0; k < 8; k++)
  53. {
  54. tmpi = i + ktmove1[k];
  55. tmpj = j + ktmove2[k];
  56. // 如果是边界了,不可走
  57. if(tmpi < 0 || tmpj < 0 || tmpi > 7 || tmpj > 7)
  58. {
  59. continue;
  60. }
  61. // 如果这个方向可走,记录下来
  62. if(board[tmpi][tmpj] == 0)
  63. {//记录方向
  64. nexti[l] = tmpi;
  65. nextj[l] = tmpj;
  66. // 可走的方向加一个
  67. l++;
  68. }
  69. }
  70. count = l;
  71. // 如果可走的方向为0个,返回
  72. if(count == 0)
  73. {
  74. return 0;
  75. }
  76. else if(count == 1)
  77. {
  78. // 只有一个可走的方向
  79. // 所以直接是最少出路的方向
  80. min = 0;
  81. }
  82. else
  83. {
  84. // 找出下一个位置的出路数
  85. for(l = 0; l < count; l++)
  86. {
  87. for(k = 0; k < 8; k++)
  88. {
  89. tmpi = nexti[l] + ktmove1[k];
  90. tmpj = nextj[l] + ktmove2[k];
  91. if(tmpi < 0 || tmpj < 0 ||
  92. tmpi > 7 || tmpj > 7)
  93. {
  94. continue;
  95. }
  96. if(board[tmpi][tmpj] == 0)
  97. {//记录下一个位置的出路数
  98. exists[l]++;
  99. }
  100. }
  101. }
  102. tmp = exists[0];
  103. min = 0;
  104. // 从可走的方向中寻找最少出路的方向
  105. for(l = 1; l < count; l++)
  106. {
  107. if(exists[l] < tmp)
  108. {
  109. tmp = exists[l];
  110. min = l;
  111. }
  112. }
  113. }
  114. // 走最少出路的方向
  115. i = nexti[min];
  116. j = nextj[min];
  117. board[i][j] = m;
  118. }
  119. return 1;
  120. }


 

文章来源: blog.csdn.net,作者:悦来客栈的老板,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq523176585/article/details/15340761

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。