回溯法是一個既帶有系統性又帶有跳躍性的搜索算法。它在包含問題的所有解的解空間樹中,按深度優先策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任意一結點時,先判斷該結點是否包含問題的解。如果肯定不包含,則跳過對該結點為根的子樹搜索,逐層向其祖先結點回溯;否則 ,進入該子樹,繼續按深度優先策略搜索。
問題的解空間
用回溯法解問題時,應明確定義問題的解空間。問題的解空間至少包含問題的一個(最優)解。對於 n=3 時的 0/1 背包問題,可用一棵完全二叉樹表示解空間,如圖所示:
求解步驟
1)針對所給問題,定義問題的解空間;
2)確定易於搜索的解空間結構;
3)以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。
常用的剪枝函數:用約束函數在擴展結點處剪去不滿足約束的子樹;用限界函數剪去得不到最優解的子樹。
回溯法對解空間做深度優先搜索時,有遞歸回溯和迭代回溯(非遞歸)兩種方法,但一般情況下用遞歸方法實現回溯法。
算法描述
解 0/1 背包問題的回溯法在搜索解空間樹時,只要其左兒子結點是一個可行結點,搜索就進入其左子樹。當右子樹中有可能包含最優解時才進入右子樹搜索。否則將右子樹剪去。
代碼:
public class Knapsack_Problem01 { double m=100; //背包最大容量 int n=5; //物品的個數 int[] w = {10,20,30,40,50}; //第i個物品的重量 int[] v = {20,30,65,40,60}; //第i個物品的價值 int[] a = new int[n]; //記錄在樹中的移動路徑,為1的時候表示選擇該組數據,為0的表示不選擇該組數據 int maxvalue = 0; //背包的最大權重值 public static void main(String[] args) { Knapsack_Problem01 p = new Knapsack_Problem01(); p.Search(0); } public void Search(int i) //i表示遞歸深度 { if(i>=n) { CheckMax(); } else { a[i] = 0; Search(i+1); a[i] = 1; Search(i+1); } } public void CheckMax() { int weight = 0; int value = 0; for(int i=0;i<n;i++) //判斷是否達到上限 { if(a[i] == 1) { weight = weight + w[i]; value = value + v[i]; } } if(weight <= m) { if(value >= maxvalue) { maxvalue = value; System.out.print("最大價值是:" + maxvalue +" "); System.out.print("所選取的物品為(1代表選中,0代表不選中): "); for(int j=0;j<n;j++) { System.out.print(a[j]); System.out.print(' '); } System.out.print('\n'); } } } }