回溯演算法思想與八皇后問題解的個數

2021-09-26 19:59:37 字數 3843 閱讀 4212

原文及更詳細的解釋參見作者部落格:

在8*8的西洋棋棋盤上,皇后是威力較大的棋子,它可以攻擊到與自己同行、同列以及同一斜線上的棋子,如下圖,所有橙色格仔上的棋子,都可能會被皇后攻擊:

而八皇后問題就是在8*8的棋盤上,找到合適的位置放置8個皇后,讓它們不會相互攻擊,而且需要找出這樣的放法共有多少種。

回溯法就是當我們確定了乙個問題的解空間的結構後,從根節點出發,以深度優先的方式去遍歷解空間,找到合適的解。所以用此方法分析八皇后問題如下:

將棋盤看作0-7的平面直角座標系,八皇后問題的解就是尋找八個點的座標(i,j)。先拋開具體問題的約束來看,我們要尋找的第乙個座標有64中可能,當第乙個座標確定後,第二個座標也有64中可能,第三個同樣如此......因此,所有可能的解形成了乙個64叉樹(模擬二叉樹的說法),這就是有可能的解(64^8種可能),解空間結構是64叉樹形狀的。

基於此解空間的結構,才能以深度優先的方式去遍歷解空間,並尋找合適的解。

當我們結合問題對解的約束來看,八皇后問題的解就是這個64叉樹上某些從根節點到葉子節點的路徑上的座標。具體約束就是皇后的攻擊規則(任意兩點不能在同一直線或斜線上)。

也就是將皇后乙個個擺上去,當我們擺到第n個皇后時,若發現按照約束條件,任何位置都不能擺放,那就說明第n-1個皇后在當前位置不能得到正確的解,所以需要重新確定第n-1個皇后的位置。若第n-1個皇后再無其他位置可擺放,則需要重新確定第n-2個皇后的位置......

這就是回溯遍歷解空間,在演算法實現時,可以使用遞迴或迭代進行回溯遍歷,分別被稱為遞迴回溯和迭代回溯。

演算法使用名為queen的二維int陣列表示棋盤,陣列的索引表示0-7的座標,值為0表示空白,值為1表示皇后的擺放位置。

由於任意一行不能有兩個皇后,所以每一行必須擺放乙個,因此程式在每一行依次嘗試:

package com.yawn.queen;

/** * @author yawn

*/public class queentest

printqueen("結果為");

}/**

* 第i行的放置

*/private static void place(int i, int start)

}if (!placed)

}/// 以下為工具方法,內容單一,淺顯易懂

/*** 輸出棋盤

*/private static void printqueen(string action)

system.out.println();}}

/*** 尋找第i行放置皇后的位置

*/private static int seek(int i)

}return -1;

}/**

* 檢查queen[i][j] 是否可以放置皇后

*/private static boolean check(int i, int j) }}

return true;

}}

求解過程中的輸出片段如下:

---> 第 5 行放置

1 0 0 0 0 0 0 0

0 0 0 0 1 0 0 0

0 1 0 0 0 0 0 0

0 0 0 0 0 0 0 1

0 0 0 0 0 1 0 0

0 0 1 0 0 0 0 0

0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0

---> 回溯到第 5 行,從 3 開始

1 0 0 0 0 0 0 0

0 0 0 0 1 0 0 0

0 1 0 0 0 0 0 0

0 0 0 0 0 0 0 1

0 0 0 0 0 1 0 0

0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0

---> 第 5 行放置

1 0 0 0 0 0 0 0

0 0 0 0 1 0 0 0

0 1 0 0 0 0 0 0

0 0 0 0 0 0 0 1

0 0 0 0 0 1 0 0

0 0 0 1 0 0 0 0

0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0

我們可以看出:第5行放置在了(5,2)位置,然後嘗試放置第六行,結果第6行無法放置,所以回溯到第5行,從(5,3)位置繼續嘗試放置,並在(5,3)放置成功。

以上**為我們找到了問題的乙個解,但我們想知道一共有多少解存在,這就需要我們稍微修改**,具體如下:

在以上**中,若找到乙個可行的解之後,程式就會執行結束。但我們若要找到所有的解,就需要在找到可行解後,繼續讓程式嘗試下乙個解即可。具體做法就是當最後一行都可以放置好皇后時,我們只記錄這個解,然後再讓程式嘗試當前位置的下乙個位置,而不退出程式。

當遍歷完所有可能的解之後,就可以停止程式。由於在遞迴呼叫過程中,會產生很深的方法棧,導致棧滿報錯,所以棋盤不宜設定太大:

八皇后問題解的個數具體實現如下:

package com.yawn.queen;

/** * @author yawn

*/public class queentest2

}/**

* 第i行的放置

*/private static void place(int i, int start)

// 第i行是否放置成功

boolean rowplaced = false;

// 遍歷第i行每個位置是否可以放置皇后

for(int j = start; j < size; j++)

break;}}

// 如果第i行沒有放置成功,則回溯到第i-1行(重新放置第i-1行)

if (!rowplaced)

}/// 以下為工具方法,內容單一,淺顯易懂

/*** 輸出棋盤

*/private static void printqueen(string string)

system.out.println();}}

/*** 去掉(i,j)位置已經放置的皇后

*/private static void reset(int i, int j)

/*** 尋找第i行放置皇后的位置

*/private static int seek(int i)

}return -1;

}/**

* 檢查queen[i][j] 是否可以放置皇后

*/private static boolean check(int i, int j) }}

}return true;

}}

---> 第91個結果,從(7,4)繼續回溯尋找...

0 0 0 0 0 0 0 1

0 0 1 0 0 0 0 0

1 0 0 0 0 0 0 0

0 0 0 0 0 1 0 0

0 1 0 0 0 0 0 0

0 0 0 0 1 0 0 0

0 0 0 0 0 0 1 0

0 0 0 1 0 0 0 0

---> 第92個結果,從(7,5)繼續回溯尋找...

0 0 0 0 0 0 0 1

0 0 0 1 0 0 0 0

1 0 0 0 0 0 0 0

0 0 1 0 0 0 0 0

0 0 0 0 0 1 0 0

0 1 0 0 0 0 0 0

0 0 0 0 0 0 1 0

0 0 0 0 1 0 0 0

結果集已經遍歷完畢,共找到結果數為:92

最終棋盤規格為8*8時,共有92個解。

回溯演算法思想與八皇后問題解的個數

原文及更詳細的解釋參見作者部落格 在8 8的西洋棋棋盤上,皇后是威力較大的棋子,它可以攻擊到與自己同行 同列以及同一斜線上的棋子,如下圖,所有橙色格仔上的棋子,都可能會被皇后攻擊 而八皇后問題就是在8 8的棋盤上,找到合適的位置放置8個皇后,讓它們不會相互攻擊,而且需要找出這樣的放法共有多少種。回溯...

回溯演算法與八皇后問題

回溯法 在遞迴構造中,生成和檢查的過程可以有機結合起來,從而減少不必要的列舉。把問題分解為若干個步驟求解時,如果當前步驟沒有合法選擇,則函式將返回上一級的遞迴呼叫,該現象稱為回溯法。所以遞迴列舉通常被稱為回溯。8皇后問題 在8 8的棋盤上放置了8個皇后,使得他們互不攻擊,每個皇后的攻擊範圍為同行,同...

八皇后問題(回溯演算法)

八皇后問題是古老的問題,十八世紀由乙個西洋棋手提出的,即在乙個8 8 的西洋棋盤上,放置八個皇后,使它們不能相互攻擊到。即不能處於同一行,同一列,也不能處於同一條斜線上,問有多少種擺法。八皇后問題是經典的回溯演算法問題,後人利用計算機,算出了8 8 的棋盤上能擺出92種,而後又提出了n皇后問題。本人...