1. 問題定義與需求分析
1、問題描述:
八皇后問題是乙個古老而著名的問題,是回溯演算法的典型例題。該問題是十九世紀著名的數學家高斯2023年提出:在8×8格的西洋棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。
基本要求:
在乙個8×8的棋盤裡放置8個皇后,要求每個皇后兩兩之間不相"衝突"(在每一橫列豎列斜列只有乙個皇后)。
1、衝突。包括行、列、兩條對角線:
(1)列:規定每一列放乙個皇后,不會造成列上的衝突;
(2)行:當第i行被某個皇后占領後,則同一行上的所有空格都不能再放皇后,要把以i為下標的行位置標為被占領狀態;
(3)對角線:對角線有兩個方向。在同一對角線上的所有點(設下標為(i,j)),要麼(i+j)是常數,要麼(i-j)是常數。因此,當第i個皇后占領了第j列後,要同時把以(i+j)、(i-j)為下標的標記置為被占領狀態。
2、資料結構。
(1)解陣列a。a[i]表示第i個皇后放置的列;範圍:1..8
(2)行衝突標記陣列b。b[i]=0表示第i行空閒;b[i]=1表示第i行被占領;範圍:1..8
(3)對角線衝突標記陣列c、d。
c[i-j]=0表示第(i-j)條對角線空閒;c[i-j]=1表示第(i-j)條對角線被占領;範圍:-7..7
d[i+j]=0表示第(i+j)條對角線空閒;d[i+j]=1表示第(i+j)條對角線被占領;範圍:2..16
1、資料初始化。
2、從n列開始擺放第n個皇后(因為這樣便可以符合每一豎列乙個皇后的要求),先測試當前位置(n,m)是否等於0(未被占領):如果是,擺放第n個皇后,並宣布占領,接著進行遞迴;如果不是,測試下乙個位置(n,m+1),但是如果當n<=8,m=8時,卻發現此時已經無法擺放時,便要進行回溯。
3、當n>8時,便一一列印出結果。
4.回溯演算法的實現
(1)為解決這個問題,我們把棋盤的橫座標定為i,縱座標定為j,i和j的取值範圍是從1到8。當某個皇后佔了位置(i,j)時,在這個位置的垂直方向、水平方向和斜線方向都不能再放其它皇后了。用語句實現,可定義如下三個整型陣列:a[8],b[15],c[24]。
其中:a[j-1]=1 第j列上無皇后
a[j-1]=0 第j列上有皇后
b[i+j-2]=1 (i,j)的對角線(左上至右下)無皇后
b[i+j-2]=0 (i,j)的對角線(左上至右下)有皇后
c[i-j+7]=1 (i,j)的對角線(右上至左下)無皇后
c[i-j+7]=0 (i,j)的對角線(右上至左下)有皇后
(2)為第i個皇后選擇位置的演算法如下:
for(j=1;j<=8;j++) /*第i個皇后在第j行*/
if ((i,j)位置為空)) /*即相應的三個陣列的對應元素值為1*/
2.概要設計
一行一行的放置皇后,所以不需要判斷行衝突。判斷列衝突很簡單,直接和前面的比一下是否一樣即可,而對於對角線衝突,就有乙個特殊的小技巧:由於每一條主對角線(x-y)是一定的,每一條副對角線(x+y)是一定的。於是,我們通過判斷那些定值與前面已經放置的皇后的定值比較即可判斷是否衝突。
在最終的n皇后求解程式中,除了演算法部分,還加入了介面優化,多次求解的詢問,以及清屏操作。
3.詳細設計
資料結構設計
a表示第i個皇后放置的列 (解陣列) b
表示列衝突標記陣列 c
、d表示對角線衝突標記陣列:處理的時候注意為了不讓陣列越界c[i+j-2] d[i-j+7] n
皇后 c[i+j-2] d[i-j+n-1]
演算法求解部分偽**如下:為第i
個皇后選擇所在行
} }
4. 編碼與測試分析
測試:
源**如下:
#include#include#includeusing namespace std;
const int maxn = 100; //n皇后最大n=100
int ans;
int a[maxn], b[maxn], c[2 * maxn], d[2 * maxn];
/* a表示第i個皇后放置的列 (解陣列)
b表示列衝突標記陣列
c、d表示對角線衝突標記陣列:處理的時候注意為了不讓陣列越界c[i+j-2] d[i-j+7]
n 皇后 c[i+j-2] d[i-j+n-1]
*/void show(int* a, int n)
cout << endl;
}void solve(int i, int n)//為第i個皇后選擇所在行
else
//回溯 把之前的標記還原 (下乙個位置可能放置成功了也可能失敗了)
b[j] = 0;
c[j + i - 2] = 0;
d[i - j + n - 1] = 0;}}
}int main()
return 0;
}
5.結束語
求解過程遇到的問題:
對於回溯位置的選取:一開始其實是不太理解應該在哪個地方進行回溯的。因為在需求中給出了整體演算法的框架,關鍵的回溯操作空出來了。基於這個問題是要求多解,因此在搜尋過程中必然需要用到回溯。值得注意的是這個問題需要回溯的情況有兩種
已經找到一解。(多解)
無法繼續求解。(求解)
第一種情況回溯實現的是多解,而第二種情況實現的是求解。理解了這一點回溯的位置自然也就很容易找到了。
對於遞迴的理解:本題對於回溯的操作是在下一步的遞迴後面還原上一步的標記(也就是回溯),一開始對於這裡也有疑惑。為什麼只還原上一步的標記,遞迴過程中每走一步狀態標記陣列都會發生變化。這裡需要明確和真正理解什麼是遞迴,它實際上是一層一層巢狀的結果,也就是說內層的狀態改變只會影響更內層的,而不會影響外層遞迴。
主要是參考這篇部落格裡面給出的n皇后答案個數,依次來驗證自己寫的程式是否正確。
n皇后問題與八皇后
這裡的n皇后問題指在乙個nxn的棋盤上放置n個棋子,使得每行每列和每條對角線上都只有乙個棋子,求其擺放的方法數。當且僅當n 1 或 n 4 時問題有解。class queens vector vector res int result 0 bool test int cur for int i 0 ...
八皇后(n皇后)
n皇后問題是經典的遞迴型問題。輸入說明 n代表皇后的數量 輸出說明 每行n個數字,一行代表乙個解。例如2413代表第1行皇后放在第2列,第2行皇后放在第4列,第3行皇后放在第1列,第4行皇后放在第3列。數字都是從1開始的。樣例輸入 4樣例輸出 2413 3142 include include us...
八皇后 n皇后 2n皇后
n n 的棋盤,棋盤中有一些位置不能放皇后。現在要向棋盤中放入 n n 個黑皇后和 n n 個白皇后,使任意的兩個黑皇后都不在同一行 同一列或同一條對角線上,任意的兩個白皇后都不在同一行 同一列或同一條對角線上。問總共有多少種放法?n n 小於等於 88。輸入的第一行為乙個整數 n n,表示棋盤的大...