0-1背包问题通常情况下物品的重量是整数的,采用动态规划可以解决,在解决物品重量非整数情况下的背包问题之前,我们先来回顾整数背包问题,并从中寻找解决非整数背包问题的方法。
问题定义:有n种物品和一个容量为$c$的背包,第$i$件物品的重量为$wi$,价格为$vi$,求出哪种物品组合放入背包使物品价值总和最大。
整数0-1背包问题
设$p(i,j)$表示在容量为j情况下,将物品$i,i+1…n$组合的放入背包的最优解的值
则其转移方程为
如果 $j>=w_i$ ,$p(i,j) = max(p(i+1,j), p(i+1,j-w_i )+v_i )$
如果 $j<w_i$ , $p(i,j) = p(i+1,j)$
可以理解为当$j<w_i$ ,背包无法放下第$i$件物品,所以其最优解和考虑放$i+1$到$n$的物品到容量为j的背包的最优解相同。当$j>=w_i$时,可以选择放和不放第$i$件物品,当不放时背包价值和$p(i+1,j)$相同,当放时背包价值为$p(i+1,j-w_i )$再加上第$i$件物品的价值。
所以整数背包问题可以采用动态规划解决,开辟$n*c$的数组,从下往上不断更新数组的值,得到$p(i,j)$的值,最优解就是$p(0,c)$的值
1 | public static int solution(int c, int[] v, int[] w){ |
路径回溯,找到最优组合
1 | private static void traceBack(int c, int[]v, int[] w, int[][] p){ |
整数0-1背包问题的改进
例如背包的容量为10,5件物品的重量分别为2,2,6,5,4 ,对应价值分别是6,3,5,4,6,则$p(i,j)$数组的更新情况如下
weight | value | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
2 | 6 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
2 | 3 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
6 | 5 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 11 |
5 | 4 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
当背包的容量很大(即c的值特别大),则这个数组将会异常的庞大,并且数组的每一个数都需要更新,算法需要的计算时间较多。
所以可以进行适当的改进,从上面的表格来看,我们无需记录数组种的每一个数,只需要记录下每行的跳跃点即可,比如最后一行从第4列开始跳跃,我们只需记录{(0,0),(4,6)}即可,倒数第二行只需记录{(0,0),(4,6),(9,10)}即可。接下来的问题是如何更新每行的跳跃点。
p[5] = {(0,0),(4,6)}
将p[5]的跳跃点加上下一个需要放入的物品的重量和价值,则可以得到q[5] = p[5]+(5,4) = {(5,4),(9,10)},
将p[5]和q[5]合并得到{(0,0),(4,6),(5,4),(9,10)},之后去除重量大却价值小的点,如(5,4)点,放入背包的物品总重量大于4,价值却只有4小于6,所以通过比较(5,4)和(4,6)需要去掉(5,4)得到p[4] = {(0,0),(4,6),(9,10)}
非整数0-1背包问题
非整数0-1背包问题可以转化为整数0-1背包问题,如果非整数可以用保留三位小数来表示的话,那么可以将非整数背包问题的所有值乘上1000,全部转为整数,采用整数背包问题解决,但是这样必须牺牲一定的精度,而且会增加开辟数组的大小(乘上1000,数组的列肯定超过1000以上),这对计算时间也有很大影响,因为需要更新每一个数组中的元素。
有效的解决方法和上述对于整数0-1背包问题的改进是一样的,而且发现上述的跳跃点并不要求是整数,对于实数一样适用,因此可以采用更新跳跃点的动态规划的方式解决非整数0-1背包问题。
1 | public static double solution(double c, double[] v, double[] w){ |
路径回溯,找到最优组合
1 | private static void traceBack(double[] v, double[] w, double[][] p, int[] head){ |
如果不考虑放入哪些物品(即不考虑路径回溯),之关心最优解的值,可以只存放当前的跳跃点集和下一步的跳跃点集,无需记录每一步的跳跃点集
1 | public static double solution2(double c, double[] v, double[] w){ |