http://blog.csdn.net/hguisu/article/details/7709276
背景:有時會遇到這樣一類題目,它的問題可以分解,但是又不能得出明確的動態規划或是遞歸解法,此時可以考慮用回溯法解決此類問題。回溯法的優點 在於其程序結構明確,可讀性強,易於理解,而且通過對問題的分析可以大大提高運行效
率。但是,對於可以得出明顯的遞推公式迭代求解的問題,還是不要用回溯 法,因為它花費的時間比較長。
回溯法中,首先需要明確下面三個概念:
(一)約束函數:約束函數是根據題意定出的。通過描述合法解的一般特征用於去除不合法的解,從而避免繼續搜索出這個不合法解的剩余部分。因此,約束函數是對於任何狀態空間樹上的節點都有效、等價的。
(二)狀態空間樹:剛剛已經提到,狀態空間樹是一個對所有解的圖形描述。樹上的每個子節點的解都只有一個部分與父節點不同。
(三)擴展節點、活結點、死結點:所謂擴展節點,就是當前正在求出它的子節點的節點,在DFS中,只允許有一個擴展節點。活結點就是通過與約束函數的對照,節點本身和其父節點均滿足約束函數要求的節點;死結點反之。由此很容易知道死結點是不必求出其子節點的(沒有意義)。
(一)約束函數:約束函數是根據題意定出的。通過描述合法解的一般特征用於去除不合法的解,從而避免繼續搜索出這個不合法解的剩余部分。因此,約束函數是對於任何狀態空間樹上的節點都有效、等價的。
(二)狀態空間樹:剛剛已經提到,狀態空間樹是一個對所有解的圖形描述。樹上的每個子節點的解都只有一個部分與父節點不同。
(三)擴展節點、活結點、死結點:所謂擴展節點,就是當前正在求出它的子節點的節點,在DFS中,只允許有一個擴展節點。活結點就是通過與約束函數的對照,節點本身和其父節點均滿足約束函數要求的節點;死結點反之。由此很容易知道死結點是不必求出其子節點的(沒有意義)。
為什么用DFS
深度優先搜索(DFS)和廣度優先搜索(FIFO)
在 分支界限法中,一般用的是FIFO或最小耗費搜索;其思想是一次性將一個節點的所有子節點求出並將其放入一個待求子節點的隊列。通過遍歷這個隊列(隊列在 遍歷過程中不斷增長)完成搜索。而DFS的作法則是將每一條合法路徑求出后再轉而向上求第二條合法路徑。而在回溯法中,一般都用DFS。為什么呢? 這是因 為可以通過約束函數殺死一些節點從而節省時間,由於DFS是將路徑逐一求出的,通過在求路徑的過程中殺死節點即可省去求所有子節點所花費的時間。FIFO 理論上也是可以做到這樣的,但是通過對比不難發現,DFS在以這種方法解決問題時思路要清晰非常多。
在 分支界限法中,一般用的是FIFO或最小耗費搜索;其思想是一次性將一個節點的所有子節點求出並將其放入一個待求子節點的隊列。通過遍歷這個隊列(隊列在 遍歷過程中不斷增長)完成搜索。而DFS的作法則是將每一條合法路徑求出后再轉而向上求第二條合法路徑。而在回溯法中,一般都用DFS。為什么呢? 這是因 為可以通過約束函數殺死一些節點從而節省時間,由於DFS是將路徑逐一求出的,通過在求路徑的過程中殺死節點即可省去求所有子節點所花費的時間。FIFO 理論上也是可以做到這樣的,但是通過對比不難發現,DFS在以這種方法解決問題時思路要清晰非常多。
回溯法可以被認為是一個有過剪枝的DFS過程
利用回溯法解題的具體步驟
首先,要通過讀題完成下面三個步驟:
(1)描述解的形式,定義一個解空間,它包含問題的所有解。
(2)構造狀態空間樹。
(3)構造約束函數(用於殺死節點)。
然后就要通過DFS思想完成回溯,完整過程如下:
(1)設置初始化的方案(給變量賦初值,讀入已知數據等)。
(2)變換方式去試探,若全部試完則轉(7)。
(3)判斷此法是否成功(通過約束函數),不成功則轉(2)。
(4)試探成功則前進一步再試探。
(5)正確方案還未找到則轉(2)。
(6)已找到一種方案則記錄並打印。
(7)退回一步(回溯),若未退到頭則轉(2)。
(8)已退到頭則結束或打印無解。
利用回溯法解題的具體步驟
首先,要通過讀題完成下面三個步驟:
(1)描述解的形式,定義一個解空間,它包含問題的所有解。
(2)構造狀態空間樹。
(3)構造約束函數(用於殺死節點)。
然后就要通過DFS思想完成回溯,完整過程如下:
(1)設置初始化的方案(給變量賦初值,讀入已知數據等)。
(2)變換方式去試探,若全部試完則轉(7)。
(3)判斷此法是否成功(通過約束函數),不成功則轉(2)。
(4)試探成功則前進一步再試探。
(5)正確方案還未找到則轉(2)。
(6)已找到一種方案則記錄並打印。
(7)退回一步(回溯),若未退到頭則轉(2)。
(8)已退到頭則結束或打印無解。
