八皇后问题

题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

这就是有名的八皇后问题。解决这个问题通常需要用递归,而递归对编程能力的要求比较高。因此有不少面试官青睐这个题目,用来考察应聘者的分析复杂问题的能力以及编程的能力。

由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标ij,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]

关于排列的详细讨论,详见本系列博客的第28篇,《字符串的排列》,这里不再赘述。

接下来就是写代码了。思路想清楚之后,编码并不是很难的事情。下面是一段参考代码:

View Code 
 int g_number = 0;
 
  
 void EightQueen()
 
 {
 
     const int queens = 8;
 
     int ColumnIndex[queens];
 
     for(int i = 0; i < queens; ++ i)
 
         ColumnIndex[i] = i;
 
  
 
     Permutation(ColumnIndex, queens, 0);
 
 }
 
  
 
 void Permutation(int ColumnIndex[], int length, int index)
 
 {
 
     if(index == length)
 
     {
 
         if(Check(ColumnIndex, length))
 
         {
 
             ++ g_number;
 
             PrintQueen(ColumnIndex, length);
 
         }
 
     }
 
     else
 
     {
 
         for(int i = index; i < length; ++ i)
 
         {
 
             int temp = ColumnIndex[i];
 
             ColumnIndex[i] = ColumnIndex[index];
 
             ColumnIndex[index] = temp;
 
  
 
             Permutation(ColumnIndex, length, index + 1);
 
  
 
             temp = ColumnIndex[index];
 
             ColumnIndex[index] = ColumnIndex[i];
 
             ColumnIndex[i] = temp;
 
         }
 
     }
 
 }
 
  
 
 bool Check(int ColumnIndex[], int length)
 
 {
 
     for(int i = 0; i < length; ++ i)
 
     {
 
         for(int j = i + 1; j < length; ++ j)
 
         {
 
             if((i - j == ColumnIndex[i] - ColumnIndex[j])
 
                 || (j - i == ColumnIndex[i] - ColumnIndex[j]))
 
             return false;
 
         }
 
     }
 
  
 
     return true;
 
 }
 
  
 
 void PrintQueen(int ColumnIndex[], int length)
 
 {
 
     printf("Solution %d\n", g_number);
 
  
 
     for(int i = 0; i < length; ++i)
 
         printf("%d\t", ColumnIndex[i]);
 
    
 
     printf("\n");
 
 }

以上转自何海涛博客

 

上面的代码存在一个问题就是计算了很多无效解,而这些无效解可以通过剪枝去掉。

对以上的代码进行略微修改,可以得到一个带剪枝的n皇后问题,其实本质上就是一个带剪枝的回溯算法。

#include <iostream>
 
 using namespace std;
 
 int g_number = 0;
 
 int Prune(int ColumnIndex[], int length, int index, int candidate_val)
 {
     for(int i=0;i<index;i++)
     {
         if((i - index == ColumnIndex[i] - candidate_val)
             || (index - i == ColumnIndex[i] - candidate_val))
             return 1;
     }
 
     return 0;
 }
 void Permutation(int ColumnIndex[], int length, int index)
 {
     if(index == length)
     {
         g_number++;
     }
     else
     {
         for(int i = index; i < length; ++ i)
         {
             if (Prune(ColumnIndex,length,index,ColumnIndex[i]))
             {
                 continue;    
             }
 
             int temp = ColumnIndex[i];
             ColumnIndex[i] = ColumnIndex[index];
             ColumnIndex[index] = temp;
 
             Permutation(ColumnIndex, length, index + 1);
 
             temp = ColumnIndex[index];
             ColumnIndex[index] = ColumnIndex[i];
             ColumnIndex[i] = temp;
         }
     }
 }
 
 int main()
 {
     for(int queens=1;queens<12;queens++)
     {
         g_number = 0;
 
         int *ColumnIndex = new int[queens];
         for(int i = 0; i < queens; ++ i)
             ColumnIndex[i] = i;
 
         Permutation(ColumnIndex, queens, 0);
 
         cout<<"queens: "<<queens;
         cout<<"\t solutions: " << g_number<<endl;
 
         delete []ColumnIndex;
     }
 
     system("pause");
 
     return 0;
 }

附上另外一个版本的n皇后回溯算法,转自unixfy的空间

// n-queens in C
 
 #include <stdio.h>
 #include <math.h>
 
 #define MAXCANDIDATES   100   /* max possible next extensions */
 #define NMAX            100   /* maximum solution size */
 
 #define TRUE    1
 #define FALSE   0
 
 typedef int Bool;
 typedef char* data;    /* type to pass data to backtrack */
 
 Bool finished = FALSE;                  /* found all solutions yet? */
 
 int solution_count;    /* how many solutions are there? */
 
 
 void process_solution()
 {
     solution_count ++;
 }
 
 int is_a_solution(int k, int n)
 {
     return (k == n);
 }
 
 
 /* What are possible elements of the next slot in the 8-queens
 problem?
 */
 
 void construct_candidates(int a[], int k, int n, int c[], int *ncandidates)
 {
     int i,j;    /* counters */
     Bool legal_move;   /* might the move be legal? */
 
     *ncandidates = 0;
     for (i=1; i<=n; i++) {
         legal_move = TRUE;
         for (j=1; j<k; j++) {
             if (abs(k-j) == abs(i-a[j])) /* diagonal threat */
                 legal_move = FALSE;
             if (i == a[j])                  /* column threat */
                 legal_move = FALSE;
         }
         if (legal_move == TRUE) {
             c[*ncandidates] = i;
             *ncandidates = *ncandidates + 1;
         }
     }
 }
 
 void backtrack(int a[], int k, int input)
 {
     int c[MAXCANDIDATES];           /* candidates for next position */
     int ncandidates;                /* next position candidate count */
     int i;                          /* counter */
 
     if (is_a_solution(k, input))
         process_solution();
     else {
         k = k+1;
         construct_candidates(a,k,input,c,&ncandidates);
         for (i=0; i<ncandidates; i++) {
             a[k] = c[i];
             backtrack(a,k,input);
             if (finished) return; /* terminate early */
         }
     }
 }
 
 int main()
 {
     int a[NMAX];    /* solution vector */
     int i;     /* counter */
 
     for (i=1; i<=12; i++) {
         solution_count = 0;
         backtrack(a,0,i);
         printf("n=%d solution_count=%d\n",i,solution_count);
     }
 
     system("pause");
 
     return 0;
 }

三:A*算法

      这种算法原本是人工智能里求解搜索问题的解法,但八皇后问题也同样是一个搜索问题,因此可以应用这种方法来进行求解。这里关于A*算法的定义以及一些概念 就不提供了,大家可以上网进行搜索,网上有很多关于A*算法的详细介绍。如果有兴趣的朋友可以借阅一本人工智能的书籍上面有关于A*算法求解八皇后问题的 详细解答过程,在此我就不多说了,我只是提供一种学习的思路。

 

四:广度优先搜索

     这个是和回溯法搜索相反的一种方法,大家如果学过数据结构的应该知道,图的搜索算法有两种,一种是深度优先搜索,二种是广度优先搜索。而前面的回溯算法回 归到图搜索的本质后,发现其实是深度优先搜索,因此必定会有广度优先搜索解决八皇后问题,由于应用广度优先搜索解决八皇后问题比应用深度优先搜索其空间复 杂度要高很多,所以很少有人去使用这种方法,但是这里我们是来学习算法的,这种算法在此不适合,不一定在其他的问题中就不适合了,有兴趣的朋友可以参考任 何一本数据结构的数据上面都有关于广度优先搜索的应用。

     总结:上面我对八皇后问题的这种算法只是起了一点抛砖引玉的作用,没有深入讲解,一是自己时间有限,二是自己研究的也不够彻底,在此只能提供一点点解题思 路希望大家看到后可以有一点前进的方向,最后说一下其实八皇后问题还可以应用一些更加高级的算法进行求解,例如遗传算法,蚁群算法,粒子群算法等等,这些 算法我还在研究中希望有兴趣的朋友可以和我交流。以上都仅为个人观点,如果错误之处请指正本人不胜感激。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值