第五周實踐專案8 8皇后問題的回溯求解 棧結構

2021-08-13 11:23:21 字數 2555 閱讀 6346

圖8.21中左邊部分給棋盤的行、列編了號,提供的擺放方法,就是問題的乙個解。右邊的部分,將各行上皇后所在的列數記錄下來,用這8個數字(4, 6, 8, 2, 7, 1, 3, 5),也構成了對問題解的一種描述。

圖8.21 8皇后問題的乙個解

由此可以看出,可以定義乙個一維陣列int x[n];,用x[i]的值表示第i行上皇后所在的列數,n皇后問題的解可以用(x[1], x[2], ….. x[n])的形式描述。

解決了資料表示的問題,設計資料處理的方法。這裡要用回溯的策略,設計計算機對n皇后問題的求解方法。以4皇后為例,如圖8.22所示,在圖8.22(a)中,第1行第1列上放置乙個皇后,圖8.22(b)中確定第2行的可能放法,在嘗試第1列、第2列由於相互攻擊而放棄之後,確定在第3列放置可以繼續,在圖8.22(c)中繼續對第3行進行考察,發現將所有4列都嘗試過了,也沒有辦法將皇后安排乙個合適的位置,對第4行做任何的嘗試都沒有意義,這時產生回溯,結果是在圖8.22(d)中將第2行的皇后安排到第4列,然後第3行的暫時可以放在第2列,在圖8.22(e)中試著確定第4行的皇后,卻發現無解再次回溯,只能夠如圖8.22(f)所示將第1行的皇后放到第2列,再經圖8.22(g)、(f)之後找到4皇后問題的乙個解,那就是圖8.22(g)的(2, 4, 1, 3)。

圖8.22 用回溯找出4皇后問題乙個解的過程

在圖8.23中,給出了求出4皇后問題所有解的完整過程的描述。圖中(1 * * *)對應圖8.22(a)中第1行皇后安排在第1列,其他行待定的狀態,接下來的(1 3 * *)對應了圖8.22(b)中第2行皇后安排在第3列的狀態。可以判斷出在這個狀態下,繼續嘗試並不能夠完成求解,於是發生回溯(其下方的b代表回溯),於是下乙個嘗試的狀態將是(1 4 * *),……。將這樣的過程繼續下去,能夠找出4皇后問題的所有解(2 4 1 3)和(3 1 4 2),如圖8.23中兩個加網格背景的結點。

圖8.23 求出4皇后問題所有解的完整過程

搞清楚用回溯法求解的過程後,將關注如何基於(x[1], x[2], ….. x[n])形式的解結構,寫出讓計算機完成求解過程的**。4皇后問題尚且可以在紙上畫出解,8皇后問題的可能解有8!=40320種,最終解有92種,必須要依靠計算機求解了。 

什麼樣的解才是可行的?需要描述出任何兩個皇后可以「互相攻擊」這樣的條件: 

(1)有兩個皇后處在同一行:解的結構(x[1], x[2], ….. x[n])已經保證同一行不會出現兩個皇后。 

(2)有兩個皇后處在同一列:表示為x[i]=x[k],假如在圖8.23中出現表示為(1 1 * *)、(4 2 3 2)之類的結點,則說明有兩個皇后在同一列了。 

(3)有兩個皇后處在同一斜線:若兩個皇后的擺放位置分別是第i行第x[i]列、第k行第x[k]列,若他們在棋盤上斜率為-1的斜線上,滿足條件i-x[i]=k-x[k],例如(1 4 3 *)、(4 1 2 *);若他們在棋盤上斜率為1的斜線上,滿足條件i+x[i]=k+x[k]。將這兩個式子分別變換成i-k=x[i]-x[k]和i-k=x[k]-x[i],例如(3 4 1 *)。綜合兩種情況,兩個皇后位於同一斜線上表示為|i-k|=|x[i]-x[k]|。 

在下面的程式實現中,place(x, k)函式用於判斷在第k行第x[k]列放置皇后,是否會與前面擺放好的皇后產生相互攻擊。只要有某行(第i行)的皇后與這個第k行的皇后處在同一列(x[i]=x[k])或者處在同一斜線(|i-k|=|x[i]-x[k]|),則立即返回假(0),表示不可能構成解。 

再接下來,就是在實現問題求解的nqueens(x, n)函式中,從第1行開始,逐行逐列地考察皇后的擺放,當遇到某一行所有可能情況試過不必再深入到下一行考察時,及時回溯到上一行,接著考察。 

程式實現中,將儲存解的陣列定義成了動態陣列。多分配乙個單元,因為陣列的首元素x[0]一直空閒未用,有用的單元是x[1]到x[n]。 

#include #include #include void nqueens(int *x, int n);     /*求解n皇后問題*/

int place(int *x, int k); /*判斷是否可以在第k行第x[k]列擺放皇后*/

void printsolution(int *x, int n); /*輸出求解結果*/

int main()

/*如果乙個皇后能放在第k行第x[k]列,則返回真(1),否則返回假(0)*/

int place(int *x, int k)

}else /*對應x[k]>n的情形,這一行已經沒有再試的必要,回溯到上一行*/

k--; /*上一行在原第x[k]列的下1列開始考察*/

}}/*輸出求解結果*/

void printsolution(int *x, int n)

printf("\n");

}printf("\n");

}

第五周實踐專案1(2)

檔名稱 asd.cpp 作 者 趙子琳 完成日期 2016年4.1日 版 本 號 v1.0 問題描述 重新定義 類,其中邏輯簡單的set和get成員函式,處理為內聯函式,直接在類內定義。include include using namespace std class void setb doubl...

第五周上機實踐專案4 靜態成員應用

檔名稱 test.cpp 完成日期 2015年 4月 7日 版本號 v1.0 問題描述 設計含有靜態資料成員和成員函式的time類。靜態資料成員是類中所有的物件共有的資料,在下面的設計中,時鐘要採用12小時制,還是要使用24小時制,顯示時,不足兩位的數字前是否前導0,都是 影響全域性 的設定,適合作...

第五周上機實踐專案2(2) 遊戲中的角色類

檔名稱 asd.cpp 作 者 趙子琳 完成日期 2016年3月29日 版 本 號 v1.0 問題描述 設計建構函式,使角色物件在建立時被初始化。include using namespace std class role role role string nam,int b role role v...