問題:
如何能夠在 n×n 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處於同一條橫行、縱行或斜線上。
分析:
這題常規的解法應該是回溯法,然而回溯法的話,要遍歷所有的情況。
這里介紹一種隨機化的算法:
我們先擺開頭的幾個棋子,然后剩下的棋子用回溯法來做,由於解空間樹的頭幾層不用拿來遍歷了,回溯的時候遍歷的結點少了很多。
研究標明,隨機擺開頭的一半略少的棋子,可以很快得得到解。當然,這個算法是只能求出一部分的解的,但是在 n 很大的時候速度比回溯法快了非常多。
ps:回溯法是可以得到所有解的。
做法:
先隨機擺頭幾個棋子,這里有一個很棒的算法,參考:http://blog.csdn.net/yusiguyuan/article/details/42607681
但是為了方便,我這里的做法是對一個數組中的所有元素做幾次隨機的交換。
void randomPlace(int n, int pieces[]) {//隨機擺放棋子
srand((unsigned)time(NULL)); for (int i = 0; i < n; i++) { int a = random(n) + 1; int b = random(n) + 1; swap(pieces[a], pieces[b]); } }
然后對接下來的棋子用回溯法:
void nQueen(int n, int t, int pieces[]) {//回溯法解n后問題
if (t > n) { resultNumber++;//計算解的個數
for (int i = 1; i <= n; i++) { for (int j = 1; j < pieces[i]; j++) cout << "- "; cout << pieces[i] << " "; for (int j = pieces[i] + 1; j <= n; j++) cout << "- "; cout << endl; } cout << endl; } else { for (int i = t; i <= n; i++) { swap(pieces[t], pieces[i]); if (isOK(t, pieces)) { nQueen(n, t + 1, pieces); } swap(pieces[t], pieces[i]); } } }
這兩個函數應該在LasVegas函數中調用,知道得出至少一個解:
void LasVegas(int n, int pieces[]) {//拉斯維加斯算法: //先隨機擺前面的一些棋子,然后后面的用回溯法來解
if (n == 1) {//特殊情況
cout << 1 << endl; } else { for (int i = 1; i <= n; i++) { pieces[i] = i; } while (resultNumber == 0) { //前幾個擺的也要合理才行
while (!isOK(n / 2, pieces)) { randomPlace(n, pieces); } nQueen(n, (n / 2)-1, pieces);//隨機一半略少的棋子擺放
} } }
代碼是從 n皇后問題改過來的,其中部分代碼我這里就不顯示出來了。