八皇后問題
一、題意解析
國際象棋中的皇后,可以橫向、縱向、斜向移動。如何在一個8X8的棋盤上放置8個皇后,使得任意兩個皇后都不在同一條橫線、豎線、斜線方向上?八皇后問題是一個古老的問題,於1848年由一位國際象棋棋手提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,如何求解?以高斯為代表的許多數學家先后研究過這個問題。后來,當計算機問世,通過計算機程序的運算可以輕松解出這個問題。
二、如何解決八皇后問題?
所謂遞歸回溯,本質上是一種枚舉法。這種方法從棋盤的第一行開始嘗試擺放第一個皇后,擺放成功后,遞歸一層,再遵循規則在棋盤第二行來擺放第二個皇后。如果當前位置無法擺放,則向右移動一格再次嘗試,如果擺放成功,則繼續遞歸一層,擺放第三個皇后......
如果某一層看遍了所有格子,都無法成功擺放,則回溯到上一個皇后,讓上一個皇后右移一格,再進行遞歸。如果八個皇后都擺放完畢且符合規則,那么就得到了其中一種正確的解法。說起來有些抽象,我們來看一看遞歸回溯的詳細過程。
1.第一層遞歸,嘗試在第一行擺放第一個皇后:
2.第二層遞歸,嘗試在第二行擺放第二個皇后(前兩格被第一個皇后封鎖,只能落在第三格):
3.第三層遞歸,嘗試在第三行擺放第三個皇后(前四格被第一第二個皇后封鎖,只能落在第五格):
4.第四層遞歸,嘗試在第四行擺放第四個皇后(第一格被第二個皇后封鎖,只能落在第二格):
5.第五層遞歸,嘗試在第五行擺放第五個皇后(前三格被前面的皇后封鎖,只能落在第四格):
6.由於所有格子都“綠了”,第六行已經沒辦法擺放皇后,於是進行回溯,重新擺放第五個皇后到第八格。:
7.第六行仍然沒有辦法擺放皇后,第五行也已經嘗試遍了,於是回溯到第四行,重新擺放第四個皇后到第七格。:
8.繼續擺放第五個皇后,以此類推......
三、八皇后問題的代碼實現
解決八皇后問題,可以分為兩個層面:
1.找出第一種正確擺放方式,也就是深度優先遍歷。
2.找出全部的正確擺放方式,也就是廣度優先遍歷。
我們本篇只介紹如何找出第一種正確擺放方式。具體代碼如下:
1 //"八皇后問題回溯實現" 2 #include <iostream> 3 using namespace std; 4 const int ArSize = 8;//這個數等於幾,就是幾皇后。 5 int num = 0; 6 void solve(bool arr[ArSize][ArSize], int row); 7 bool check(bool arr[ArSize][ArSize], int row, int column); 8 void outPut(bool arr[ArSize][ArSize]); 9 10 int main() 11 { 12 bool chessboard[ArSize][ArSize]; 13 // 數組初始化 14 for (auto &i : chessboard) 15 { 16 for (auto &j : i) 17 { 18 j = false; 19 } 20 } 21 solve(chessboard, 0); 22 cout << "八皇后問題共有" << num << "種解!" << endl; 23 system("pause"); 24 return 0; 25 } 26 // 回溯法 27 void solve(bool arr[ArSize][ArSize], int row) 28 { 29 for (int column = 0; column < ArSize; ++column) 30 { 31 arr[row][column] = true; 32 if (check(arr, row, column)) 33 { 34 if (row + 1 == ArSize) 35 { 36 outPut(arr); 37 } 38 else 39 { 40 solve(arr, row + 1); 41 } 42 } 43 arr[row][column] = false; 44 } 45 } 46 // 判斷皇后的落點是否合規 47 bool check(bool arr[ArSize][ArSize], int row, int column) 48 { 49 if (row == 0) 50 { 51 return true; 52 } 53 int i, j; 54 // 判斷縱向是否有沖突 55 for (i = 0; i < row; ++i) 56 { 57 if (arr[i][column]) 58 { 59 return false; 60 } 61 } 62 i = row - 1; 63 j = column - 1; 64 // 判斷正斜對角線是否有沖突 65 while (i >= 0 && j >= 0) 66 { 67 if (arr[i][j]) 68 { 69 return false; 70 } 71 --i; 72 --j; 73 } 74 i = row - 1; 75 j = column + 1; 76 // 判斷負斜對角線是否有沖突 77 while (i >= 0 && j <= ArSize - 1) 78 { 79 if (arr[i][j]) 80 { 81 return false; 82 } 83 --i; 84 ++j; 85 } 86 return true; 87 } 88 // 打印每種正確的解法 89 void outPut(bool arr[ArSize][ArSize]) 90 { 91 ++num; 92 cout << "**********************" << num << "*********************" << endl; 93 for (int i = 0; i < ArSize; ++i) 94 { 95 for (int j = 0; j < ArSize; ++j) 96 { 97 cout << arr[i][j] << " "; 98 } 99 cout << endl; 100 } 101 cout << "*********************************************" << endl; 102 }
輸出結果的部分截圖如下:
參考資料:
http://www.cnblogs.com/yonggandefeng/p/6275861.html