回溯算法_01揹包問題_Java實現

轉載請註明出處: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));
}

}


發佈了39 篇原創文章 · 獲贊 14 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章