1. 程式人生 > >0-1揹包問題—回溯演算法—java實現

0-1揹包問題—回溯演算法—java實現

0-1揹包問題

【問題描述】

有n種可選物品1,…,n ,放入容量為c的揹包內,使裝入的物品具有最大效益。
表示
n :物品個數
c :揹包容量
p1,p2, …, pn:個體物品效益值
w1,w2, …,wn:個體物品容量

【問題解析】

0-1揹包問題的解指:物品1,…,n的一種放法(x1, ···,xn的0/1賦值),使得效益值最大。
假定揹包容量不足以裝入所有物品:面臨選擇
【優化原理】無論優化解是否放物品1,優化解對物品2,…,n的放法,相對剩餘揹包容量,也是優化解。

首先給出所需要的變數:
```
private static int[] p;//物品的價值陣列
private static int[] w;//物品的重量陣列
private static int c;//最大可以拿的重量
private static int count;//物品的個數

private static int cw;//當前的重量
private static int cp;//當前的價值
static int bestp;//目前最優裝載的價值
private static int r;//剩餘物品的價值

private static int[] cx;//存放當前解
private static int[] bestx;//存放最終解
```
解空間樹:子集樹
可行性約束條件:cw + w[t] < c
上界函式:cp + r <= bestp,即如果當前結點滿足這個條件時,就可以將該結點的右子樹剪去。

[核心演算法]

```
/**
 * 回溯
 * @param t
 */
public static void BackTrack(int t) {
    if(t>count) {//到達葉結點
        if(cp>bestp) {
            for(int i = 1;i<=count;i++) {
                bestx[i] = cx[i];
            }

            bestp = cp;
        }
        return;
    }

    r -= p[t];
    if(cw + w[t] <= c) {//搜尋左子樹
        cx[t] = 1;
        cp += p[t];
        cw += w[t];
        BackTrack(t+1);
        cp -= p[t];//恢復現場
        cw -= w[t];//恢復現場

    }

    if(cp + r >bestp) {//剪枝操作
        cx[t] = 0;//搜尋右子樹
        BackTrack(t+1);
    }
    r += p[t];//恢復現場
}

```

[完整程式碼]

```
package sort;

public class Zero_One {
private static int[] p;//物品的價值陣列
private static int[] w;//物品的重量陣列
private static int c;//最大可以拿的重量
private static int count;//物品的個數

private static int cw;//當前的重量
private static int cp;//當前的價值
static int bestp;//目前最優裝載的價值
private static int r;//剩餘物品的價值

private static int[] cx;//存放當前解
private static int[] bestx;//存放最終解

public static int Loading(int[] ww,int[] pp, int cc) {
    //初始化資料成員,陣列下標從1開始
    count = ww.length - 1;
    w = ww;
    p = pp;
    c = cc;
    cw = 0;
    bestp = 0;
    cx = new int[count+1];
    bestx = new int [count+1];

    //初始化r,即剩餘最大價格
    for(int i = 1;i<=count;i++) {
        r += p[i];
    }

    //呼叫回溯法計算
    BackTrack(1);
    return bestp;   
}

/**
 * 回溯
 * @param t
 */
public static void BackTrack(int t) {
    if(t>count) {//到達葉結點
        if(cp>bestp) {
            for(int i = 1;i<=count;i++) {
                bestx[i] = cx[i];
            }

            bestp = cp;
        }
        return;
    }

    r -= p[t];
    if(cw + w[t] <= c) {//搜尋左子樹
        cx[t] = 1;
        cp += p[t];
        cw += w[t];
        BackTrack(t+1);
        cp -= p[t];//恢復現場
        cw -= w[t];//恢復現場

    }

    if(cp + r >bestp) {//剪枝操作
        cx[t] = 0;//搜尋右子樹
        BackTrack(t+1);
    }
    r += p[t];//恢復現場
}



public static void main(String[] args) {
    //測試
    int[] w1 = {0,15,25,40,20,15,24};
    int[] p1 = {0,10,5,20,2,14,23};
    int c1 = 30;
    Loading(w1,p1,c1);
    System.out.println("最優裝載為:" + bestp);
    for(int i =1;i<=count;i++) {
        System.out.print(bestx[i] + " ");
    }           
}

[執行示例]

執行