状态空间树
狀態空間樹:
就是問題的解空間樹,分為子集樹和排列樹
子集樹
當所給的問題是從n個元素組成的集合set中找到滿足某一條件的一個子集時,相應的解空間樹稱為子集。
要注意,這個解空間樹是一個虛擬的樹,并不是構建出來的,如下面這顆樹:
有三個物品n = 3,(類似于0-1背包問題)
xi = {0、1}表示第 i 個物品 ni 是否選中,
xi = 0 表示未選中,xi = 1表示選中
樹有三層,第 i 層表示物品 ni,數字1、0表示 xi 的值
葉子上的個節點到A的路線代表了一個子集樹——一種可能的解法,如 H 代表了111,表示n1、n2、n3都被選中,I表示110,n1、n2被選中,n3未被選中。
以下用兩個例子來解釋解空間樹
0-1背包中使用窮舉法
本文先從簡單的窮舉法開始介紹子集樹
窮舉法得到每一個子集樹
- 若當前為非葉節點
將1壓棧
遞歸訪問左子樹
(遞歸訪問左子樹遞歸到最后時,例如訪問到 H 時,就要pop了)
將0壓棧
遞歸訪問右子樹
(遞歸訪問右子樹遞歸到最后時,例如訪問到 I 時,就要pop了) - 若當前為葉節點
順序打印棧中內容
排列樹
TSP(Travelling Saleman Problem,貨郎擔、郵遞員)問題、或者求全排列的問題。
以全排列為例:
定義:
設Set{S1, S2, S3,…, Sn}有 n 個元素,Seti = Set - {Si}
子集合 X 中元素的全排列記為 arrange(X),
(Si)arrange(Seti)表示在全排列arrange(Seti)的每一個排列前加上前綴Si得到的排列。
Seti表示Set中沒有Si元素
Set的全排列可歸納如下:
當n=1時,arrange(Set)=(S1),其中S1是集合Set中唯一的元素;
當n>1時,arrange(Set)由(S1)arrange(Set1),…, (Sn)arrange(Setn)構成。
實現思想:
將整組數中的所有的數分別與第一個數交換,這樣就總是在處理后n-1個數的全排列。
有ABC三個字母,求其全排列:
當n = 3,并且Set = {a,b,c},則:
arrange(Set)=a.arrange({b,c}) + b.arrange({a,c}) + c.arrange({a,b})
arrange({b,c})=b.arrange? + c.arrange(b)
a.arrange({b,c})=ab.arrange? + ac.arrange(b)
=ab.c + ac.b=(abc, acb)
全排列
public class Tsp {static int[] s;static void arrangeTree(int i, int n) {// 為什么是 i < n -1。因為最后一個不用交換了if (i < n - 1) {for (int j = i; j < n; j++) {// swap(s[i], s[j]);int k = s[j];s[j] = s[i];s[i] = k;arrangeTree(i + 1, n);// 這里swap,是因為arrangeTree(i + 1, n);前的swap影響了數組 s 的順序。// swap(s[i], s[j]);k = s[j];s[j] = s[i];s[i] = k;}} else {for (int j : s) {System.out.print(j);}System.out.println();}}public static void main(String[] args) {int n = 3;s = new int[n];for (int i = 0; i < n; i++) {s[i] = i + 1;}arrangeTree(0, n);}}emmmm,全排列這個其實挺迷的
總結
- 遞歸簡單,但如何將問題分解成遞歸就不容易了
- 遞歸的判出界限讓人很迷
總結
- 上一篇: Allegro-PCB文件默认双击打开
- 下一篇: FLTK-Rs