八皇后問題 (源於 劉汝佳的《演算法競賽入門經典(第2版)》)
在棋盤上放置8個皇后,使得它們互不攻擊,此時每個皇后的攻擊範圍為同行同列和同對角線,要求找出所有解。(這裡只輸出解的個數)
【分析】
棋盤為8行8列,可想而知,放置8個皇后,想讓8個皇后互不攻擊,那麼它們至少每一個都要放置在不同行不同列中,即恰好每行每列各放置一個皇后。如果用C[x]表示第x行皇后的列編號,則問題變成了全排列產生問題。就是列編號0~7進行全排列,而每個列編號則依次儲存在C[x]中,那麼第x行皇后就有對應的列編號。在遞迴枚舉的過程中,當我們發現該解不合法的時候,就停止對該解繼續遞迴的操作,而且返回上一級的遞迴調用繼續求解。(回溯)
註:當把問題分成若干步驟並遞迴求解時,如果當前步驟沒有合法選擇,則函數將返回上一級遞迴調用,這種現象稱為回溯。
【程式】
用C++語言編寫程式,代碼如下:
(兩種求解八皇后問題的方法)
#include<iostream>using namespace std;int tot;const int n = 8;int C[n];void search1(int cur) {if (cur == n) tot++; //遞迴到邊界。只要走到這裡,所有皇后必然不衝突else {for (int i = 0; i < n; i++) {int ok = 1;C[cur] = i; //嘗試把第cur行的皇后放在第 i 列for (int j = 0; j < cur; j++) //檢查是否和前面的皇后衝突if (C[j] == C[cur] || cur - C[cur] == j - C[j] || cur + C[cur] == j + C[j]) {ok = 0;break;}if (ok)search1(cur + 1); //如果合法,則繼續遞迴}}}int vis[3][100];void search2(int cur) {if (cur == n) tot++;else for (int i = 0; i < n; i++) {//利用二維數組直接判斷//vis數組表示已經放置的皇后佔據了哪些列、主對角線和副對角線if (!vis[0][i] && !vis[1][cur + i] && !vis[2][cur - i + n]) { C[cur] = i; //如果不用 列印解,整個C數組都可以省略vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 1; //修改全域變數search2(cur + 1);vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 0; //恢複原狀}}}int main() {tot = 0;search1(0);cout << "search1 : " << endl;cout << tot << endl;tot = 0;search2(0);cout << "search2 : " << endl;cout << tot << endl;return 0;}