八皇后问题

转自:http://blog.csdn.net/justme0/article/details/7540425


背景介绍:

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。而且仅当 n = 1 或 n ≥ 4 时问题有解。

摘自百度百科


1、介绍

先上张图来说明用回溯法八皇后问题的每一步:

        


2、程序

看着严蔚敏的书写的,写好后运行一次性成功了,程序如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //  N皇后问题  
  2.   
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. #define N 8  
  7.   
  8. bool matrix[N + 1][N + 1] = {0};  
  9.   
  10. bool IsLegal(bool matrix[N + 1][N + 1], const int &i, const int &j)  
  11. {  
  12.     //  判断前面的i-1个棋子与matrix[i][j]是否冲突,i为1时合法  
  13.   
  14.     for (int m = 1; m <= i - 1; ++m) {  
  15.         for (int n = 1; n <= N; ++n) {   //  实际每一行只有一个棋子  
  16.             if (matrix[m][n] == 1) {  
  17.                 if ( n == j || abs(i - m) == abs(j - n) )   //  key, not bad  
  18.                     return false;  
  19.             }  
  20.         }  
  21.     }  
  22.     return true;  
  23. }  
  24.   
  25. void Print(bool matrix[N + 1][N + 1])  
  26. {  
  27.     static int count = 1;  
  28.     printf("Case %d:\n", count++);  
  29.     for (int i = 1; i <= N; i++) {  
  30.         for (int j = 1; j <= N; j++) {  
  31.             matrix[i][j] == 1 ? printf("%c ", 2) : printf(". ");  
  32.         }  
  33.         cout << endl;  
  34.     }  
  35.     cout << endl;  
  36. }  
  37.   
  38. void Trial(const int i)  
  39. {  
  40.     //  进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子  
  41.     //  现从第i行起继续为后续棋子选择合适位置  
  42.   
  43.     if (i > N)   //  输出当前的合法布局  
  44.         Print(matrix);  
  45.     else  
  46.         for (int j = 1; j <= N; ++j) {  
  47.             matrix[i][j] = 1;  
  48.             if ( IsLegal(matrix, i, j) )  
  49.                 Trial(i + 1);  
  50.             matrix[i][j] = 0;  
  51.         }  
  52. }  
  53.   
  54. int main(void)  
  55. {  
  56.     Trial(1);  
  57.   
  58.     return 0;  
  59. }  

运行结果:



3、数学问题

关于n皇后的解的个数(8皇后是92个解):

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. n       a(n)  
  2. 1       1  
  3. 2       0  
  4. 3       0  
  5. 4       2  
  6. 5       10  
  7. 6       4  
  8. 7       40  
  9. 8       92  
  10. 9       352  
  11. 10      724  
  12. 11      2680  
  13. 12      14200  
  14. 13      73712  
  15. 14      365596  
  16. 15      2279184  
  17. 16      14772512  
  18. 17      95815104  
  19. 18      666090624  
  20. 19      4968057848  
  21. 20      39029188884  
  22. 21      314666222712  
  23. 22      2691008701644  
  24. 23      24233937684440  
  25. 24      227514171973736  
  26. 25      2207893435808352  
  27. 26      22317699616364044  

独立解的问题我就不多提了。目前这个数列还没找到通项公式。有意思的是,高斯算八皇后的解的个数时,他算错了,他的答案是76种,不知道他漏了哪种,呵呵。(不过也是4的倍数)


4、想法

那个Trial递归函数我还没弄明白,对着书抄的,要是自己想,难。还有待研究推广。



2012/5/8 更新

把判断是否合法的IsLegal函数优化了,原来的程序是O(N^3),现在是 O(N^2):

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //  N皇后问题  
  2.   
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. #define N 8  
  7.   
  8. int matrix[N + 1][N + 1] = {0};  
  9. //  matrix[0][j]为空,matrix[i][0]中放第i行的皇后的列坐标(从1开始记)  
  10.   
  11. bool IsLegal(const int &i, const int &j)  
  12. {  
  13.     //  判断前面的i-1个棋子(坐标是matrix[m][n])与matrix[i][j]是否冲突,i为1时合法  
  14.     for (int m = 1; m <= i - 1; ++m) {  
  15.         int n = matrix[m][0];  
  16.         if (  n == j || abs(i - m) == abs(j - n) )  
  17.             return false;  
  18.     }  
  19.     return true;  
  20. }  
  21.   
  22. void Print(void)  
  23. {  
  24.     static int count = 1;  
  25.     printf("Case %d:\n", count++);  
  26.     for (int i = 1; i <= N; i++) {  
  27.         for (int j = 1; j <= N; j++) {  
  28.             matrix[i][j] == 1 ? printf("%c ", 2) : printf(". ");  
  29.         }  
  30.         cout << endl;  
  31.     }  
  32.     cout << endl;  
  33. }  
  34.   
  35. void Trial(const int &i)  
  36. {  
  37.     //  进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子  
  38.     //  现从第i行起继续为后续棋子选择合适位置  
  39.   
  40.     if (i > N)   //  输出当前的合法布局  
  41.         Print();  
  42.     else  
  43.         for (int j = 1; j <= N; ++j) {  
  44.             matrix[i][j] = 1;  
  45.             if ( IsLegal(i, j) ) {  
  46.                 matrix[i][0] = j;  
  47.                 Trial(i + 1);  
  48.             }  
  49.             matrix[i][j] = 0;  
  50.         }  
  51. }  
  52.   
  53. int main(void)  
  54. {  
  55.     Trial(1);  
  56.   
  57.     return 0;  
  58. }  


2012/5/12更新:

推荐:http://topic.csdn.net/t/20060424/13/4709025.html

我在自己机子上运行了下:




5、python实现


N = 8
case_cnt = 0

pos_list = [0] * N

def eight_queen(number):
    global N
    N = number

    global case_cnt
    case_cnt = 0

    global pos_list
    pos_list = [0] * number
    
    deal_deploy(0)
    print('%d ------> %d\n' % (number, case_cnt))

def deal_deploy(row):
    if row >= N:
        deal_print()
    else:
        for cl in range(N):
            pos_list[row] = cl
            if not conflict(row, cl):
                deal_deploy(row + 1)

            pos_list[row] = 0

def deal_print():
    global case_cnt
    case_cnt += 1
    
    str = 'case %d:\n' % case_cnt
    for i in range(N):
        str += ' . ' * (pos_list[i]) + ' # ' + ' . ' * (N - pos_list[i] - 1) + '\n'

    print(str)

def conflict(row, cl):

    for r in range(row):
        c = pos_list[r]
        if cl == c or abs(row - r) == abs(cl - c):
            return True

    return False


eight_queen(6)


##for i in range(1, 11):
##    eight_queen(i)


输出:

case 1:
 .  #  .  .  .  . 
 .  .  .  #  .  . 
 .  .  .  .  .  # 
 #  .  .  .  .  . 
 .  .  #  .  .  . 
 .  .  .  .  #  . 


case 2:
 .  .  #  .  .  . 
 .  .  .  .  .  # 
 .  #  .  .  .  . 
 .  .  .  .  #  . 
 #  .  .  .  .  . 
 .  .  .  #  .  . 


case 3:
 .  .  .  #  .  . 
 #  .  .  .  .  . 
 .  .  .  .  #  . 
 .  #  .  .  .  . 
 .  .  .  .  .  # 
 .  .  #  .  .  . 


case 4:
 .  .  .  .  #  . 
 .  .  #  .  .  . 
 #  .  .  .  .  . 
 .  .  .  .  .  # 
 .  .  .  #  .  . 
 .  #  .  .  .  . 


6 ------> 4


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值