答:回溯的實質是在問題的解空間進行深度優先搜索。DFS是個圖的算法,但是回溯算法的圖在哪里呢?我們把解空間的一個狀態當做一個節點,由於解空間非常龐大,這個圖就大到無法想象了。
舉個例子吧,比如全排列問題,對於n個元素進行全排列,一共有n!種可能,比如n=9時,一共有9! = 362880種排列。初始化,我們什么都沒有,定義如下狀態
#define PT_SIZE 9
class PTState
{
int m_solution[PT_SIZE];
int m_arranged;
};
其中m_arranged表示已經在m_solution中排列的數字,剛開始時啥都沒做,當然是m_arranged為0啦,而m_solution這個數組是用來放要排列的數字的,比如
123456789對應於m_solution[i] = i + 1
那么接下來要做啥呢?當然是開始排第一個數了,也就是要填m_solution[0]啦,這時你有多少種選擇呢?嗯,當然是1, 2, 3, 4, 5 ,6, 7,8,9種,這還要說嗎?那么先嘗試誰呢?
從小到小依次填好了。
下面這個圖是n=3時的解空間搜索圖,其中白色的節點是初始節點,一個數都沒填,第一行全是問號,第二行有個標記,表示還沒有排列一個數。當第一位選1的時,節點變成了紫色,此時m_arranged = 1, 還剩兩個數沒有填。這時填第二個數時,發現只能填2或者3,當填上2后,又到了下面一個藍色的節點,該節點的狀態中m_arranged = 2,再擴展下去,第三位只能填3,於是到了最下面的黃色節點。
此時,再想擴展黃色節點時,發現m_arranged已經是3了,這時表明這是一個最終的節點,無法再擴展了,於是得到了一個排列。那么其他排列如何得到呢?回到黃色節點上面的藍色節點,我們發現第三位除了3再也不能填其他數了,沒法繼續擴展,沒辦法,再回到上面的紫色節點,這時發現,紫色節點中的第二位還能填3,於是可以順利擴展到下面的第二個藍色節點。這個過程便是回溯和擴展節點的主要過程。
在編程實現時,由於我們無法存儲所有的狀態節點,只能保持一個當前狀態,然后通過擴展到下一個節點,來修改當前節點的狀態,但是當擴展完一個節點時,必須得恢復到原來節點的狀態,才能進行下一個擴展。這個邏輯寫成代碼便是如下形式:
Solve (State & state)
for action in A
if state.CanMove(action)
state.Forward(action);
Solve(state)
state.Recover(action);