回溯演算法 --- 例題4.N皇后問題
阿新 • • 發佈:2021-12-21
一.問題描述
N*N棋盤上放置N個皇后使得每個皇后互不受攻擊. 即任二皇后不能位於同行同列和同一斜線上.
如四皇后問題的兩個解:
二.解題思路
將棋盤從左至右,從上到下編號為1,...,n,皇后編號為1,...,n.
設解為(x1, ..., xn) , xi為皇后i的列號,且xi位於第i行.
解空間:E={ (x1,..., xn) | xi∈Si, i=1,...,4}, Si={1, ..., 4},1 <= i <= n
解空間為排列樹? 是的!N個皇后在N*N的棋盤中一共有C(N^2, N)种放置情況,我們需要找到其中滿足條件的排列情況.
其約束集合D為:
- xi ≠ xj ,保證皇后i, j不在同一行中
- xi - i ≠ xj - j ,保證皇后i, j不在同一斜線上
- xi + i ≠ xj +j ,保證皇后i, j不在同一斜線上
後兩條總結一下就是 abs(i-j) ≠ abs(x[i] - x[j])
用回溯法解N後問題時,用完全n叉樹表示解空間.可行性約束Place減去不滿足行,列和斜線約束的子樹.
具體程式碼如下:
// N皇后問題回溯演算法 #include<bits/stdc++.h> using namespace std; class Queen { friend int NQueen(int) ; private: bool Place(int k); void Print(); void Backtrack(int t); int n; //皇后個數 int *x; //當前解 long sum; //當前已經找到的可行方案數 }; void Queen::Print() { cout<<"放置方案為: "; for(int i=1; i<=n; i++) cout<<x[i]<<" "; cout<<endl; } bool Queen::Place(int k) //放置第k行 { for(int j=1; j<k; j++) //第j行 { if(abs(k-j) == abs(x[j]-x[k]) || (x[j]==x[k])) //不在同一斜線(左斜線右斜線, 不在同一列) return false; } return true; } void Queen::Backtrack(int t) //t表示第t個皇后,她 { if(t > n) { sum++; Print(); } else { for(int i=1; i<=n; i++) //遍歷第t行每一列 { x[t] = i; if(Place(t)) //如果第t行能夠放置 { Backtrack(t+1); } } } } int NQueen(int n) { Queen X; //初始化X X.n = n; X.sum = 0; int *p = new int[n+1]; for(int i=0; i<=n; i++) p[i] = 0; X.x = p; X.Backtrack(1); X.Print(); delete [] p; return X.sum; } int main() { cout<<"請輸入皇后個數: "; int n; while(cin>>n && n) { int ans = NQueen(n); cout<<"放置方案數: "<<ans<<endl; cout<<"請輸入皇后個數: "; } system("pause"); return 0; }
執行結果如下:
參考畢方明老師《演算法設計與分析》課件.
歡迎大家訪問個人部落格網站---喬治的程式設計小屋,一起加油!