演算法系列 N皇后問題

2022-03-15 20:57:30 字數 3634 閱讀 3921

常規n皇后解決問題過程

一.問題描述

運用回溯法解題通常包含以下三個步驟:

(1)針對所給問題,定義問題的解空間;

(2)確定易於搜尋的解空間結構;

(3)以深度優先的方式搜尋解空間,並且在搜尋過程中用剪枝函式避免無效搜尋;

通過上述的基本思路,我們可以將問題描述為:x(j)表示乙個解的空間,j表示行數,裡面的值表示可以放置在的列數,抽象約束條件得到能放置乙個皇后的約束條件(1)x(i)!=x(k);(2)abs(x(i)-x(k))!=abs(i-k)。應用回溯法,當可以放置皇后時就繼續到下一行,不行的話就返回到第一行,重新檢驗要放的列數,如此反覆,直到將所有解解出。

也就是對於n×n的棋盤,選擇出n個符合i!=r∧j!=s∧|i-r|!=|j-s|∨(i+r)!=(j+s)的點的排列總數。

二.偽**:

判斷點是否符合要求:

place(k, x)

i=1while iif x[i]==x[k] or abs(x[i]-x[k])==abs(i-k) then

return false

i=i+1

return true

求問題的所有解:

nqueens(n, x)

sum=0 , x[1]=0 , k=1

while k>0 do

x[k]=x[k]+1

while x[k]<=n and !(place(k,x))

x[k]=x[k]+1

if x[k]<=n then

sum=sum+1

else

k=k+1 ,x[k]=0

else

k=k-1

print sum

三.**實現

1 #include 2

using

namespace

std;

3 #include 45/*

檢查可不可以放置乙個新的皇后*/6

bool place(int k, int *x)717

return

true;18

}1920/*

求解問題的所有解的總數,x存放列數

*/21

void nqueens(int n,int *x)

2241

else

4246

else

47 k=k-1;48

}49 cout<

解的總數為:

"5152

intmain()

53

四.實驗結果

五.存在的問題

當皇后個數n大於等於16以上,程式對棋盤的掃瞄次數大到驚人:

從維基百科列出的結果不難看出,在25皇后時,符合條件的解集已經如此龐大了。而陣列的儲存及加法運算來求解已經不能適應當前的運算。

六.演算法改進

程式中的所有數在計算機記憶體中都是以二進位制的形式儲存的,而位運算就是直接對整數在記憶體中的二進位制位進行操作,所以速度快,效率高。因此我們選擇用位運算來改進運算速度。

演算法思想用圖列應該更好解釋:

如上圖所示,假設乙個8*8的棋盤,那麼第一次我們在棋盤第乙個位置放置乙個皇后,則此時,第二列最靠右可放棋子的位置是3。假設第二個放到第二列3的位置,則此時,第三列最靠右能放棋子的位置是5...我們用藍色線代表向右邊斜的線,用橙色代表向左邊斜的線,用紅色代表向下邊的線,而同一行,我們不需判斷,因為棋子不能放置同一行的位置。這樣,我們畫了上面的圖,所有被紅,橙,藍穿過的格都不能放置皇后。那麼從圖上,我們很容易的推出第四行第幾個位置能放皇后(從右往左算是2,7,8)。

我們用0代表沒有被線穿過,用1代表被線穿過,用row代表豎方向,ld代表左斜線,rd代表右斜線。假設每次放皇后我們都先放最靠右邊的。

則放第乙個皇后時:

row=0000 0001, ld=0000 0001, rd=0000 1001

放置第二個皇后時:

row=0000 0101, ld=0000 0110, rd=0000 0100

放置第三個皇后時:

row=0001 0101, ld=0001 1100, rd=0001 0010

…按照圖,我們可以標識出有沒有被線穿過的格仔,那麼我們要在上面放皇后,當然要放置在沒有被線穿過的位置:也就是說row 或者 ld 或者 rd上有被線穿過的格仔都是不符合要求的,用數學描述為:

(row|ld|rd),因為數學上經常以1為是,0為否,所以我們將式子改為:~(row|ld|rd)

而初始時,某一行還沒有線的限制,所以都是可以放置皇后的,對於8皇后,初始時,我們可以定義upperlimit=1111 1111來表示。

則要判斷當前行那些位置可以放置皇后,我們可以用:

pos=upperlimit&~(row|ld|rd)

一直放置,直到無可放置的位置或者掃瞄完一次棋盤為止。

對於無可放置位置這種情況,我們則要回溯到上一步,然後再找上一行可放置皇后的另乙個點,如果不存在該點,則再繼續向上回溯…重複直到找出所有解。

而對於掃瞄完成,我們如何判斷呢?從上圖,我們很容易直到,每次放置乙個皇后,row則會多乙個1,所以,只要到row=upperlimit時,說明棋盤掃瞄結束,則我們找到符合結果的總數sum就要加一。

程式清單:

1 #include 2

using

namespace

std;

3 #include 4

5int sum = 0;6

int upperlimit = 1;7

void compare(int row,int ld,int

rd)818}

19else22}

2324

intmain()

25

實驗結果:

改進後演算法的不足:雖然運算速度及效率提高了很多倍,但是由於上n大於等於20後,運算量太大,改進運算方式不能從本質上解決問題,所以我們繼續跟進。

七.演算法改進二

改進思路,對於不同的皇后問題,使用不同的方法計算,

如,對於除2、3、8、9、14、15、26、27、38、39之外的任意n值皇后,可以用分治法,如: 

如圖,我們可以用模擬法來推算除去上述特殊值的n皇后問題,但是其推導公式過於複雜,分類運算考慮的情況及排列組合的公式還沒完全推導出,所以這個演算法還只是停留在我們的思路中。

演算法系列 八皇后問題

public class queen public void printlocation private boolean isoccupied int i,int j public void setlocation int i,int j,int flag public void place int...

演算法 n皇后問題

題目描述 乙個 n n 的棋盤,要在上面放 n 個皇后。規則 兩個皇后之間如果是同列 同行 同對角線它們會互相攻擊。也就 是 說 棋盤上的任意兩個皇后不能為同列 同行 同對角線。演算法思想 q j 表示乙個解的空間即儲存一組可行解的陣列,j表示行數,q j 的值表示j行可以放置皇后的所在列數,根據任...

(演算法)N皇后問題

八皇后問題 在8 x 8的西洋棋上擺放八個皇后,使其不能相互攻擊,即任意兩個皇后不得處於同一行,同一列或者同意對角線上,求出所有符合條件的擺法。1 回溯法 資料結構 由於8個皇后不能處在同一行,那麼肯定每個皇后佔據一行,這樣可以定義乙個陣列a 8 陣列中第i個數字,即a i 表示位於第i行的皇后的列...