常規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行的皇后的列...