0-1背包问题

条件:
有若干个物品,重量分别为w1, w2, w3 .....wi, 将这些物品装入一个最大承受重量为capacity的袋子中,求最大能装入的重量。

我们试着用回溯算法解决这个问题,回溯算法其实就是一个一个的试,将所有情况都试一遍,最终找到满足条件的那个值。

实现如下:

int[] elements = {3, 1, 5, 2, 4}  //物品重量
int capacity=9; //袋子容量
int cw = 0; //当前袋子的重量
int max = 0; //
for (int i = 0; i < elements.length; i++) {//从第一个元素开始,遍历所有可能性
       f(i, elements[i]);
}
//
void f(int i, int cw) {
        if (cw == capacity || i == elements.length - 1) {
            if (cw > max) {
                max = cw;
            }
            return;
        }
        f(i + 1, cw);//下一个不放
        int cw2 = cw + elements[i + 1];
        if (cw2 <= capacity) {
            f(i+1, cw2); //下一个放
        }
}

f(i, cw)表示从第i个元素开始放入去寻找满足条件的最大重量,cw表示当前袋子已放入的重量,每运行完一次f(i,cw),总能确定一个max。对于上述例子,总体上运行5次f(i, cw)就能确定最大的max。

如下图表示,其中红色节点表示下一个物品不放入袋中,此时cw不变,max也不变;绿色表示下一个物品放入袋中。对于给定的i和cw,f(i, cw)总能计算出一个max(=到现在为止最大的那个cw)。黄色节点即为满足条件的节点。

从第一个元素开始放入的所有可能性

从上图可以看出,当放入第1,4,5或第1,2,3个元素时满足条件;


从第二个元素开始放入的所有可能性

从这个图可以看出,当从第二个元素开始放入时,放入第2,3,4时可以使重量最大。

从上面两个图中我们也可以发现,在计算f(0,3)和f(1,1)时出现了很多相同的中间状态,比如f(4,5)、f(4,8)等,若把这些中间状态保存起来,后面使用时就不必再计算了,这样肯定能提高效率。
优化f(i, cw):

int[][] his = new int[elements.length][capacity+1];//数组默认值都为0
void f(int i, int cw) {
        if(his[i][cw] > 0) {//当前参数条件下,max已计算过,不用再计算了
            return;
        } else {
            his[i][cw] = 1; //留下痕迹,标识计算过f(i, cw)
        }
        if (cw == capacity || i == elements.length - 1) {
            if (cw > max) {
                max = cw;
            }
            return;
        }
        f(i + 1, cw);//下一个不放
        int cw2 = cw + elements[i + 1];
        if (cw2 <= capacity) {
            f(i+1, cw2); //下一个放
        }
}

二维数组中的值并没有实际意义,因此可以用boolean类型代替,减少内存占用

boolean[][] his = new boolean[elements.length][capacity+1];//数组默认值都为0
void f(int i, int cw) {
        if(his[i][cw]) {//当前参数条件下,max已计算过,不用再计算了
            return;
        } else {
            his[i][cw] = true; //留下痕迹,标识计算过f(i, cw)
        }
        if (cw == capacity || i == elements.length - 1) {
            if (cw > max) {
                max = cw;
            }
            return;
        }
        f(i + 1, cw);//下一个不放
        int cw2 = cw + elements[i + 1];
        if (cw2 <= capacity) {
            f(i+1, cw2); //下一个放
        }
}

你可能感兴趣的:(0-1背包问题)