八皇后

递归与回溯:
   a.回溯算法的基本思想:从问题的某一种状态出发,搜索可以到达的所有状态。当某个状态到达后,可向前回退,并继续搜索其他可达状态。当所有状态都到达后,回溯算法结束!
   b. 对于回溯算法,在前面KMP匹配中就利用了这个思想,只不过当时KMP中定义了一个node数组(起到了一个地图的作用,记录了每种回溯情况的可能)。而这节中,是利用函数的活动对象保存回溯算法的状态数据,因此可以利用递归完成回溯算法!
2.八皇后问题:
   a.问题背景:国际象棋是一个8*8的矩阵,在棋盘中同时放下8个皇后,且互相不攻击的情况叫八皇后问题
   b.如图,说明八皇后问题中的回溯算法:
      
注意:其实就是不断的通过递归函数,去往棋盘中尝试放皇后,成功就继续递归(即继续放皇后),失败就跳出递归函数,回溯到上层递归函数中,上层递归函数中保存着上一个皇后的位置!!!这就是八皇后中,回溯的概念!
    c. 八皇后的算法思路:
       第一、为了更加方便我们表示棋盘,我们使用一个10*10的二维数组来表示8*8的棋盘加棋盘边框。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. char board[10][10];  
       第二、初始化棋盘,把棋盘边框标记为'#',把棋盘中的位置都标记为‘ 空格 ’
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void init()  
  2. {  
  3.     int i = 0;  
  4.     int j = 0;  
  5.       
  6.     for(i=0; i<N+2; i++)  
  7.     {  
  8.         board[0][i] = '#';  
  9.         board[N+1][i] = '#';  
  10.         board[i][0] = '#';  
  11.         board[i][N+1] = '#';  
  12.     }  
  13.       
  14.     for(i=1; i<=N; i++)  
  15.     {  
  16.         for(j=1; j<=N; j++)  
  17.         {  
  18.             board[i][j] = ' ';  
  19.         }  
  20.     }  
  21. }  
       第三、定义一个棋盘的打印函数display(),方便测试
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void display()  
  2. {  
  3.     int i = 0;  
  4.     int j = 1;  
  5.       
  6.     for(i=0; i<N+2; i++)  
  7.     {  
  8.         for(j=0; j<N+2; j++)  
  9.         {  
  10.             printf("%c", board[i][j]);  
  11.         }  
  12.           
  13.         printf("\n");  
  14.     }  
  15. }  
      第四、 我们定义一个检查的函数check(),来判断这个位置是否可以放皇后,对于检查,我们检查三个方向,左上(-1,-1) 、 右上(-1,1)  、正上(-1,0)   对于横排我们是不检测的,因为我们每一排就放一个皇后,皇后用' * '来表示!
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct _tag_pos //定义一个数据结构来充当方向   
  2. {  
  3.     int i;  
  4.     int j;  
  5. }Pos;  
  6. /*检测三个方向  左上  右上  正上   横排是不检测的 因为一排只放一个*/   
  7. static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};  
  8.   
  9. int check(int i, int j)  
  10. {  
  11.     int p = 0;   
  12.     int ret = 1;  
  13.     for(p = 0; p < 3; p++) //检测三个方向   
  14.     {  
  15.         int ni = i;  
  16.         int nj = j;  
  17.           
  18.         while(ret && (board[ni][nj] != '#'))//判断没有到达棋盘边界   
  19.         {  
  20.             ni = ni + pos[p].i;  
  21.             nj = nj + pos[p].j;  
  22.               
  23.             ret = ret && (board[ni][nj] != '*');//判断这个方向没有放过皇后   
  24.         }  
  25.     }   
  26.     return ret; //可以放皇后返回1  不可返回0   
  27. }  
      第五、 是八皇后算法的核心,就是放皇后的函数find(),这个函数是这样的!先从第一行开始,进行对列数j的for循环,在for循环中判断(i,j)是否可以放皇后,一旦可以放皇后,就放置一个皇后,然后find(i+1),进入第二行(即第二层递归)。继续做同样的判断,这个递归函数有两个出口,同理,就有两种情况结束递归,一种是,i超过了第八行,也就是说,皇后全部放置完全,所以应该display()打印!另一种是,for循环结束,依然没有check()成功,说明前面有皇后放错了!导致这一行不能放皇后!应该进行回溯!!!所以结束这个错误的递归,返回到上一层递归,清除上层递归中放错的皇后   board[i][j] = ' ';  继续进行上一行的for循环!!!这里需要补充的是,即使i超过了第八行,皇后放置完全了,函数依然也会返回到上一行,去进行上一行的for循环,擦除皇后位置,尝试寻找新八皇后的情况!!!
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void find(int i)  
  2. {  
  3.     int j = 0;  
  4.     if(i > N) //判断是否已经超过了第八行   
  5.     {  
  6.         coust++; //计算八皇后情况的个数   
  7.         display();   
  8.         //getchar();  
  9.     }  
  10.     else  
  11.     {  
  12.         for(j = 1; j <= N; j++) //判断一行 是否有匹配的位置   
  13.         {  
  14.             if(check(i,j))  
  15.             {  
  16.                 board[i][j] = '*'//放置皇后   
  17.                 find(i+1);  
  18.                 board[i][j] = ' '//清除放错的皇后   
  19.             }  
  20.         }  
  21.     }  
  22. }   
注意:之所以说,八皇后中利用递归来进行回溯,就是因为当递归结束的时候,会返回到上一层放置皇后的位置(即栈空间中,保存了上行皇后i,j的情况)
看着上面的动态想象下,当有时候,出现过的皇后又消失了,就是因为for循环结束了,还没有check成功,导致递归结束,返回到上层递归,并且擦除上层皇后的位置,并且继续for循环,如果这层for中check依然不成功,继续回溯,这个消失的过程就是回溯过程!与动图不同的是,我们的程序,不会在找完一种情况后就结束!即使成功匹配,程序也会向上层回溯,去寻找其它的情况,直到第一行for循环结束(即 i=1 的时候),程序才会停止!

本节代码:

八皇后问题的完整代码:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #define N 8  
  4.   
  5. static char board[N+2][N+2];  
  6. static int coust = 0; //记录八皇后个数   
  7. typedef struct _tag_pos //定义一个数据结构来充当方向   
  8. {  
  9.     int i;  
  10.     int j;  
  11. }Pos;  
  12. /*检测三个方向  左上  右上  正上   横排是不检测的 因为一排只放一个*/   
  13. static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};  
  14.   
  15.    
  16. void init()  
  17. {  
  18.     int i = 0;  
  19.     int j = 0;  
  20.       
  21.     for(i=0; i<N+2; i++)  
  22.     {  
  23.         board[0][i] = '#';  
  24.         board[N+1][i] = '#';  
  25.         board[i][0] = '#';  
  26.         board[i][N+1] = '#';  
  27.     }  
  28.       
  29.     for(i=1; i<=N; i++)  
  30.     {  
  31.         for(j=1; j<=N; j++)  
  32.         {  
  33.             board[i][j] = ' ';  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. void display()  
  39. {  
  40.     int i = 0;  
  41.     int j = 1;  
  42.       
  43.     for(i=0; i<N+2; i++)  
  44.     {  
  45.         for(j=0; j<N+2; j++)  
  46.         {  
  47.             printf("%c ", board[i][j]);  
  48.         }  
  49.           
  50.         printf("\n");  
  51.     }  
  52. }  
  53.   
  54. int check(int i, int j)  
  55. {  
  56.     int p = 0;   
  57.     int ret = 1;  
  58.     for(p = 0; p < 3; p++) //检测三个方向   
  59.     {  
  60.         int ni = i;  
  61.         int nj = j;  
  62.           
  63.         while(ret && (board[ni][nj] != '#'))//判断没有到达棋盘边界   
  64.         {  
  65.             ni = ni + pos[p].i;  
  66.             nj = nj + pos[p].j;  
  67.               
  68.             ret = ret && (board[ni][nj] != '*');//判断这个方向没有放过皇后   
  69.         }  
  70.     }   
  71.     return ret; //可以放皇后返回1  不可返回0   
  72. }  
  73.    
  74. void find(int i)  
  75. {  
  76.     int j = 0;  
  77.     if(i > N) //判断是否已经超过了第八行   
  78.     {  
  79.         coust++; //计算八皇后情况的个数   
  80.         display();   
  81.         //getchar(); //起到断点的作用   
  82.     }  
  83.     else  
  84.     {  
  85.         for(j = 1; j <= N; j++) //判断一行 是否有匹配的位置   
  86.         {  
  87.             if(check(i,j))  
  88.             {  
  89.                 board[i][j] = '*'//放置皇后   
  90.                 find(i+1);  
  91.                 board[i][j] = ' '//清除放错的皇后   
  92.             }  
  93.         }  
  94.     }  
  95. }   
  96. int main()  
  97. {  
  98.     init();  
  99.     //display();  
  100.     find(1);   
  101.     printf("coust is %d \n",coust);  
  102.     return 0;  
  103. }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值