八皇后问题(递归回溯算法详解+C代码)

ゝ一纸荒年。 2022-03-22 15:12 388阅读 0赞
  1. **为了理解“递归回溯”的思想,我们不妨先将4位皇后打入冷宫,留下剩下的4位安排进4x4的格子中且不能互相打架,有多少种安排方法呢?现在我们把第一个皇后放在第一个格子,被涂黑的地方是不能放皇后的:**

12

  1. **第二行的皇后只能放在第三格或第四格,比如我们放在第三格:**

放第二个皇后
这样一来前面两位皇后已经把第三行全部锁死了,第三位皇后无论放在第三行的哪里都难逃被吃掉的厄运。于是在第一个皇后位于第一格,第二个皇后位于第三格的情况下此问题无解。所以我们只能返回上一步,来给2号皇后换个位置:
给第二个皇后换位置
此时,第三个皇后只有一个位置可选。当第三个皇后占据第三行蓝色空位时,第四行皇后无路可走,于是发生错误,则返回上层调整3号皇后,而3号皇后也别无可去,继续返回上层调整2号皇后,而2号皇后已然无路可去,则再继续返回上层调整1号皇后,于是1号皇后往后移一格位置如下,再继续往下安排:
回溯重新安排一号皇后
核心代码如下:

  1. void EightQueen( int row )
  2. {
  3. int col;
  4. if( row>7 ) //如果遍历完八行都找到放置皇后的位置则打印
  5. {
  6. Print(); //打印八皇后的解
  7. count++;
  8. return ;
  9. }
  10. for( col=0; col < 8; col++ ) //回溯,递归
  11. {
  12. if( notDanger( row, col ) ) // 判断是否危险
  13. {
  14. chess[row][col]=1;
  15. EightQueen(row+1);
  16. chess[row][col]=0; //清零,以免回溯时出现脏数据
  17. }
  18. }
  19. }

我们来重点看一下这段代码:

  1. **第一次进来,row=0,意思是要在第一行摆皇后,只要传进来的row参数不是8,表明还没出结果,就都不会走if里面的return,那么就进入到for循环里面,col0开始,即第一列。此时第一行第一列肯定合乎要求(即notDanger方法肯定通过),能放下皇后,因为还没有任何其他皇后来干扰。**
  2. **关键是notDanger方法通过了之后,在if里面又会调用一下自己(即递归),row加了1,表示摆第二行的皇后了。第二行的皇后在走for循环的时候,分两种情况,第一种情况:for循环没走到头时就有通过notDanger方法的了,那么这样就顺理成章地往下走再调用一下自己(即再往下递归),row再加1(即摆第三行的皇后了,以此类推)。第二种情况:for循环走到头了都没有通过notDanger方法的,说明第二行根本一个皇后都摆不了,也触发不了递归,下面的第三行等等后面的就更不用提了,此时控制第一行皇后位置的for循环col1,即第一行的皇后往后移一格,即摆在第一行第二列的位置上,然后再往下走,重复上述逻辑。**
  3. **注意,一定要添加清零的代码,它只有在皇后摆不下去的时候会执行清0的动作(避免脏数据干扰),如果皇后摆放很顺利的话从头到尾是不会走这个请0的动作的,因为已经提前走if里面的return方法结束了。**
  4. **总之,这段核心代码很绕,原理一定要想通,想个十几二十遍差不多就能理解其中的原理了,递归回溯的思想也就不言而喻了。八皇后问题一共有92种情况**

完整代码如下:

  1. #include <stdio.h>
  2. int count = 0;
  3. int chess[8][8]={0};
  4. int notDanger( int row, int col )
  5. {
  6. int i,k;
  7. // 判断列方向
  8. for( i=0; i < 8; i++ )
  9. {
  10. if( chess[i][col]==1 )
  11. {
  12. return 0;
  13. }
  14. }
  15. // 判断左对角线
  16. for( i=row, k=col; i>=0 && k>=0; i--, k-- )
  17. {
  18. if(chess[i][k]==1 )
  19. {
  20. return 0;
  21. }
  22. }
  23. // 判断右对角线
  24. for( i=row, k=col; i>=0 && k<8; i--, k++ )
  25. {
  26. if(chess[i][k]==1 )
  27. {
  28. return 0;
  29. }
  30. }
  31. return 1;
  32. }
  33. void Print() //打印结果
  34. {
  35. int row,col;
  36. printf("第 %d 种\n", count+1);
  37. for( row=0; row < 8; row++ )
  38. {
  39. for( col=0; col< 8; col++ )
  40. {
  41. if(chess[row][col]==1) //皇后用‘0’表示
  42. {
  43. printf("0 ");
  44. }
  45. else
  46. {
  47. printf("# ");
  48. }
  49. }
  50. printf("\n");
  51. }
  52. printf("\n");
  53. }
  54. void EightQueen( int row )
  55. {
  56. int col;
  57. if( row>7 ) //如果遍历完八行都找到放置皇后的位置则打印
  58. {
  59. Print(); //打印八皇后的解
  60. count++;
  61. return ;
  62. }
  63. for( col=0; col < 8; col++ ) //回溯,递归
  64. {
  65. if( notDanger( row, col ) ) // 判断是否危险
  66. {
  67. chess[row][col]=1;
  68. EightQueen(row+1);
  69. chess[row][col]=0; //清零,以免回溯时出现脏数据
  70. }
  71. }
  72. }
  73. int main()
  74. {
  75. EightQueen(0);
  76. printf("总共有 %d 种解决方法!\n\n", count);
  77. return 0;
  78. }

发表评论

表情:
评论列表 (有 0 条评论,388人围观)

还没有评论,来说两句吧...

相关阅读

    相关 解决皇后回溯算法

    问题简述:八皇后问题是一个古来而著名的问题,该问题是19世纪著名的数学家高斯同学提出来的。在8\8的国际象棋上摆放八个皇后,使其不能互相的攻击,也就是说,任意的两个皇后不能放在