八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。
历史 八皇后问题最早是由国际象棋棋手马克斯·贝瑟尔(Max Bezzel)于1848年提出。第一个解在1850年由弗朗兹·诺克(Franz Nauck)给出。并且将其推广为更一般的n皇后摆放问题。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。
在此之后,陆续有数学家对其进行研究,其中包括高斯和康托,1874年,S.冈德尔提出了一个通过行列式来求解的方法[2],这个方法后来又被J.W.L.格莱舍加以改进。
1972年,艾兹格·迪杰斯特拉用这个问题为例来说明他所谓结构化编程的能力[3]。他对深度优先搜索回溯算法有着非常详尽的描述2。
八皇后问题在1990年代初期的著名电子游戏《第七访客》和NDS平台的著名电子游戏《雷顿教授与不可思议的小镇》中都有出现。
解题方法 八个皇后在8x8棋盘上共有4,426,165,368(64C8)种摆放方法,但只有92个可行(皇后间互不攻击)的解。如果将旋转和对称的解归为一种的话,则一共有12个独立解,具体如下:
解的个数 下表给出了n皇后问题的解的个数包括独立解U(OEIS数列A002562)以及可行解D(OEIS数列A000170)的个数:
可以注意到六皇后问题的解的个数比五皇后问题的解的个数要少。现在还没有已知公式可以对n计算n皇后问题的解的个数。
示例程序 下面是求解n皇后的C代码,在程序中可以自己设置n个皇后以及选择是否打印出具体解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #define QUEENS 8 #define IS_OUTPUT 1 int A[QUEENS + 1 ], B[QUEENS * 3 + 1 ], C[QUEENS * 3 + 1 ], k[QUEENS + 1 ][QUEENS + 1 ];int inc, *a = A, *b = B + QUEENS, *c = C;void lay (int i) { int j = 0 , t, u; while (++j <= QUEENS) if (a[j] + b[j - i] + c[j + i] == 0 ) { k[i][j] = a[j] = b[j - i] = c[j + i] = 1 ; if (i < QUEENS) lay(i + 1 ); else { ++inc; if (IS_OUTPUT) { for (printf ("(%d)\n" , inc), u = QUEENS + 1 ; --u; printf ("\n" )) for (t = QUEENS + 1 ; --t; ) k[t][u] ? printf ("Q " ) : printf ("+ " ); printf ("\n\n\n" ); } } a[j] = b[j - i] = c[j + i] = k[i][j] = 0 ; } } int main (void ) { lay(1 ); printf ("%d皇后共计%d个解\n" , QUEENS, inc); return 0 ; }
以下列出尼克劳斯·维尔特的Pascal语言程序。此程序找出了八皇后问题的一个解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 program eightqueen1(output); var i : integer; q : boolean; a : array [ 1 .. 8 ] of boolean; b : array [ 2 .. 16 ] of boolean; c : array [ -7 .. 7 ] of boolean; x : array [ 1 .. 8 ] of integer; procedure try ( i : integer; var q : boolean) ; var j : integer; begin j := 0 ; repeat j := j + 1 ; q := false; if a[ j] and b[ i + j] and c[ i - j] then begin x[ i ] := j; a[ j ] := false; b[ i + j] := false; c[ i - j] := false; if i < 8 then begin try ( i + 1 , q); if not q then begin a[ j] := true; b[ i + j] := true; c[ i - j] := true; end end else q := true end until q or (j = 8 ); end ; begin for i := 1 to 8 do a[ i] := true;for i := 2 to 16 do b[ i] := true;for i := -7 to 7 do c[ i] := true;try ( 1 , q);if q then for i := 1 to 8 do write ( x[ i]:4 ); writeln end .
使用回溯法进行求解八皇后问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <stdio.h> #define PRINTF_IN 1 int queens (int Queens) { int i, k, flag, not_finish=1 , count=0 ; int a[Queens+1 ]; i=1 ; a[1 ]=1 ; printf ("%d皇后的可能配置是:" ,Queens); while (not_finish){ while (not_finish && i<=Queens){ for (flag=1 ,k=1 ; flag && k<i; k++) if (a[k]==a[i]) flag=0 ; for (k=1 ; flag&&k<i; k++) if ( (a[i]==a[k]-(k-i)) || (a[i]==a[k]+(k-i)) ) flag=0 ; if (!flag){ if (a[i]==a[i-1 ]){ i--; if (i>1 && a[i]==Queens) a[i]=1 ; else if (i==1 && a[i]==Queens) not_finish=0 ; else a[i]++; }else if (a[i] == Queens) a[i]=1 ; else a[i]++; }else if (++i<=Queens) if (a[i-1 ] == Queens ) a[i]=1 ; else a[i] = a[i-1 ]+1 ; } if (not_finish){ ++count; if (PRINTF_IN){ printf ((count-1 )%3 ? " [%2d]:" : "\n[%2d]:" , count); for (k=1 ; k<=Queens; k++) printf (" %d" , a[k]); } if (a[Queens-1 ]<Queens ) a[Queens-1 ]++; else a[Queens-1 ]=1 ; i=Queens -1 ; } } return count; } int main () { int Num ; printf ("输入一个N皇后数值:" ); scanf ("%d" , &Num); printf ("\n%d皇后有%d种配置\n" ,Num,queens(Num)); }
使用回溯法进行求解八皇后问题(Java版本),可直接复制到 N-Queens - LeetCode (页面存档备份,存于互联网档案馆) 测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 class Solution { public List<List<String>> solveNQueens (int n) { List<List<String>> results = new ArrayList <>(); char [][] result = new char [n][n]; for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < n; ++j) { result[i][j] = '.' ; } } backtrack(results, result, 0 ); return results; } private static void backtrack (List<List<String>> results, char [][] result, int x) { for (int j = 0 ; j < result.length; ++j) { if (isValid(result, x, j)) { result[x][j] = 'Q' ; if (x == result.length - 1 ) { showResult(results, result); } else { backtrack(results, result, x + 1 ); } result[x][j] = '.' ; } } } private static boolean isValid (char [][] result, int x, int y) { for (int i = 0 ; i < x; ++i) { if (result[i][y] == 'Q' ) { return false ; } } for (int i = x - 1 , j = y - 1 ; i >= 0 && j >= 0 ; --i, --j) { if (result[i][j] == 'Q' ) { return false ; } } for (int i = x - 1 , j = y + 1 ; i >= 0 && j < result.length; --i, ++j) { if (result[i][j] == 'Q' ) { return false ; } } return true ; } private static void showResult (List<List<String>> results, char [][] result) { List<String> list = new ArrayList <>(result.length); for (char [] value : result) { list.add(new String (value)); } results.add(list); } }
c++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include "iostream" #include "cmath" using namespace std; #define Max 20 int a[Max];int show (int S) { int i,p,q; int b[Max][Max]={0 }; for (i=1 ;i<=S;i++) { b[i][a[i]]=1 ; printf ("(%d,%d)\t" ,i,a[i]); } printf ("\n" ); for (p=1 ;p<=S;p++) { for (q=1 ;q<=S;q++) { if (b[p][q]==1 ) printf ("x" ); else printf ("o" ); } printf ("\n" ); } return 0 ; } int check (int k) { int i; for (i=1 ;i<k;i++) { if ((a[i]==a[k]) || (a[i]-a[k]==k-i)|| (a[i]-a[k]==i-k) ) { return 0 ; } } return 1 ; } void check_m (int num) { int k=1 ,count=0 ; printf ("N皇后問題的所有解(包含經由旋轉的解):\n" ); a[k]=1 ; while (k>0 ) { if (k<=num && a[k]<=num) { if (check (k)==0 ) { a[k]++; } else { k++; a[k]=1 ; } } else { if (k>num) { count++; printf ("[%d]: " ,count); show (num); } k--; a[k]++; } } printf ("總共有 %d \n" ,count,"個" ); } int main (void ) { int N,d; do { printf (" N皇后問題的解(N<20) \n" ); printf ("請輸入N的值:_" ); scanf ("%d" ,&N); printf ("\n" ); if (N>0 &&N<20 ) { check_m (N); break ; } else { printf ("輸入錯誤,請重新輸入" ); printf ("\n\n" ); break ; } } while (1 ); system ("pause" ); return 0 ; }
大众文化
电脑游戏《第七访客》中,伊格(Ego,玩家)在史陶夫的府邸的游戏室里碰到的象棋问题正是八个皇后问题。
原文地址:https://zh.wikipedia.org/wiki/%E5%85%AB%E7%9A%87%E5%90%8E%E9%97%AE%E9%A2%98
在知识共享 署名-相同方式共享 3.0协议 之条款下提供