部分背包问题
部分背包求的是最大值,里面的物品可以分割,所以我们只需要求出物品的性价比,然后再排序就可以解决。
0-1背包问题
0-1背包,问题就在于你放还是不放。
假如我有三个物品:A(重:1,价值:2元)B(重:2,价值:4元)C(重:3,价值:3元)
然后我的背包只能承重 3. 那肯定是选 a和b了。因为 两个价值加起来有6元. 如果只放c,价值只有3元。
但是 我的背包能承重 5呢,ab还是6,但是bc就是7了,为了利益,所以要选择bc。
这就是0-1背包的痛点,因为要考虑重量是否符合背包。以及如何使价值最大化。
解决思路(动态规划)
我们假设 value 表示背包的总价值,k 表示放进去的物品的编号(这里我规定物品编号从1开始)。C 表示当前背包的当前的重量。
所以我们可以用一个共识来表示 value = B(k,C) 。B为一个函数,可以看作 将1到k的物品放入背包并且规定当前背包的容量,就可以得到他们当前最大的价值。
然后我们可以推断出。
包不够大
如果 第k个物品要放进去的时候,当前容量是C。C+K的重量>背包的重量那么:
B(k,C) = B(k-1,C)
包够
如果不大于则表示可以放入。
那么 则有两种情况,一个是放入,一个是不放入
包够大,且可以放入
放入那它的价值 value =B(k,C)=B(k-1,C-Ck) + Vk
注:Ck是K的重量,Vk是K的价值,C为当前背包容量
可能有疑惑的是:为什么要C-Ck,因为这种考虑是像K放入的时候,可能会将之前放入的几个物品或者全部物品排挤出去,但不能保证是否是可以保值的
。
包够大,但不放
那么 B(k,C) = B(k-1,C)
放与不放做比较,取最大值
所以在可以放的情况下,B(k,C) = max(B(k-1,C), B(k,C)=B(k-1,C-Ck) + Vk)
以上面的abc为例子:
横坐标是包的容量,从0开始到3 是背包的依次递增的容量
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
a(1) | 0 | 2 | 2 | 2 |
b(2) | 0 | 0 | 4 | 6 |
c(3) | 0 | 0 | 4 | 6 |
java代码实现:
public static int fun(int[] m, int[] v, int bagSize) {
// m 和 v 都是从下标1开始
int[][] B = new int[m.length + 1][bagSize + 1];
int k = 1;
for (; k <= m.length; k++) {
for (int c = 1; c <= bagSize; c++) {
// 超重了
if (m[k - 1] + c > bagSize) {
B[k][c] = B[k - 1][c];
}
if (c >= m[k - 1]) {
B[k][c] = Integer.max(B[k - 1][c], B[k - 1][c - m[k - 1]] + v[k - 1]);
}
}
}
for (int i = 0; i <= m.length; i++) {
for (int j = 0; j < bagSize + 1; j++) {
System.out.printf("%3d",B[i][j]);
}
System.out.print("\n");
}
return B[m.length][bagSize];
}
public static void main(String[] args) {
int[] m = {1, 2, 3};
int[] v = {2, 4, 3};
int bagSize = 3;
int fun = fun(m, v, bagSize);
System.out.println(fun);
}