轉載請註明出處:http://blog.csdn.net/ljmingcom304/article/details/50314839
本文出自:【樑敬明的博客】
1.回溯算法
回溯算法也叫試探法,通俗的將就是一個方向的路一直往前走,能走則走,不能走則退回來換一個方向再試。一般的實現步驟是:針對一個問題定義解的空間,至少包含問題的一個最優解;用易於搜索的解空間結構,使得能用回溯方法搜索整個解空間;以深度優先的方式搜索整個解空間,並在搜索過程中通過剪枝函數避免無效搜索。
如上圖所示,每個節點均有左右兩種選擇,最終要實現結果會產生AH、AI、AJ、AK、AL、AM、AN和AO八種實現方案,需要確定哪種方案爲最優方案。以左邊的條件爲首要條件進行判斷,那麼其搜索路線爲:【A】→【B】→【D】→【H】→【I】→【E】→【J】→【K】→【C】→【F】→【L】→【M】→【G】→【N】→【O】,即在全部滿足限制條件的前提下,實現每個節點之間的兩兩比較,最終選擇出最優方案。
2.01揹包問題
一個旅行者有一個最多能裝m公斤的揹包,現在有n中物品,每件的重量分別是W1、W2、……、Wn,每件物品的價值分別爲C1、C2、……、Cn, 需要將物品放入揹包中,要怎麼樣放才能保證揹包中物品的總價值最大?
3.算法分析
上述問題包含兩種情況,一種是將物品放入揹包中,另一種是不將物品放入揹包中。其中隱含着三個限制條件。第一個限制條件爲放入物品的總和不能超過揹包的總承重,即當放入的物品總和超過揹包的總承重時,即使揹包中物品的總價值再大也毫無意義。第二個限制條件爲要保證揹包中物品的總價值最大。無論第一個條件成不成立,第二個條件總是在其之上的一種更優的選擇。第三個限制條件爲當達到物品的數量上限,即沒有可以獲取物品再放入揹包時,前兩個限制條件均毫無意義,因此需要優先進行判斷。
這裏我們可以將第一種情況看做左節點,第二種情況看做右節點,第三個限制條件用於終結節點的搜索。因此在解決該問題時,首先需要判斷是否有物品可以放到揹包中,要是沒有物品可以放到揹包中,直接返回結果,當前的價值即爲揹包的最優價值。然後判斷左節點是否爲可行節點,當左節點可行,就優先搜索左子樹,不滿足則進入右子樹。當達到物品的數量上限,直接返回當前物品數量的結果;若左右節點同時不可行,則直接將結果返回上一步。以此類推,將結果從下至上一層一層進行返回,得到問題的最優結果。
創建一個物品對象,分別存在價值、重量以及單位重量價值三種屬性。
public class Knapsack implements Comparable<Knapsack> {
/** 物品重量 */
private int weight;
/** 物品價值 */
private int value;
/** 單位重量價值 */
private int unitValue;
public Knapsack(int weight, int value) {
this.weight = weight;
this.value = value;
this.unitValue = (weight == 0) ? 0 : value / weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getUnitValue() {
return unitValue;
}
@Override
public int compareTo(Knapsack snapsack) {
int value = snapsack.unitValue;
if (unitValue > value)
return 1;
if (unitValue < value)
return -1;
return 0;
}
}
按照回溯算法將物品放入揹包中。
public class HSSFProblem {
// 待選擇的物品
private Knapsack[] bags;
// 揹包的總承重
private int totalWeight;
// 揹包的當前承重
private int currWeight;
// 待選擇物品數量
private int n;
// 放入物品後背包的最優價值
private int bestValue;
// 放入物品和揹包的當前價值
private int currValue;
public HSSFProblem(Knapsack[] bags, int totalWeight) {
this.bags = bags;
this.totalWeight = totalWeight;
this.n = bags.length;
// 物品依據單位重量價值從大到小進行排序
Arrays.sort(bags, Collections.reverseOrder());
}
public int solve(int i) {
// 當沒有物品可以放入揹包時,當前價值爲最優價值
if (i >= n) {
bestValue = currValue;
return bestValue;
}
// 首要條件:放入當前物品,判斷物品放入揹包後是否小於揹包的總承重
if (currWeight + bags[i].getWeight() <= totalWeight) {
// 將物品放入揹包中的狀態
currWeight += bags[i].getWeight();
currValue += bags[i].getValue();
// 選擇下一個物品進行判斷
bestValue = solve(i + 1);
// 將物品從揹包中取出的狀態
currWeight -= bags[i].getWeight();
currValue -= bags[i].getValue();
}
// 次要條件:不放入當前物品,放入下一個物品可能會產生更優的價值,則對下一個物品進行判斷
// 當前價值+剩餘價值<=最優價值,不需考慮右子樹情況,由於最優價值的結果是由小往上逐層返回,
// 爲了防止錯誤的將單位重量價值大的物品錯誤的剔除,需要將物品按照單位重量價值從大到小進行排序
if (currValue + getSurplusValue(i + 1) > bestValue) {
// 選擇下一個物品進行判斷
bestValue = solve(i + 1);
}
return bestValue;
}
// 獲得物品的剩餘總價值
public int getSurplusValue(int i) {
int surplusValue = 0;
for (int j = i; j < n; j++)
surplusValue += bags[i].getValue();
return surplusValue;
}
}
最終測試結果:90
public class HSSFTest {
public static void main(String[] args) {
Knapsack[] bags = new Knapsack[] { new Knapsack(2, 13),
new Knapsack(1, 10), new Knapsack(3, 24), new Knapsack(2, 15),
new Knapsack(4, 28), new Knapsack(5, 33), new Knapsack(3, 20),
new Knapsack(1, 8) };
int totalWeight = 12;
HSSFProblem problem = new HSSFProblem(bags, totalWeight);
System.out.println(problem.solve(0));
}
}