问题描述
有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
物品数量4 背包容量8
物品 | 1 | 2 | 3 | 4 |
重量 | 2 | 3 | 4 | 5 |
价值 | 3 | 4 | 5 |
解决方案
j表示当前背包容量,i表示当前物品,v表示物品价值,w表示物品重量,table[i][j]表示第i件物品在容量为j的情况下的最大价值
1、解决边界问题,当背包容量为0或物品数量为0时,背包的总价值为0
2、判断当前物品是否能放入背包(背包是否能将当前重量的物品放入),w[i]<=j此时分两种情况:
2.1、不能放入,当前的物品重量大于上背包容量
2.2、能放入,当前的物品重量小于上背包容量,能放入分两种情况:
2.2.1、放,当前物品的价值加上上一件物品除去当前物品重量的最大价值大于上一件物品的最大价值时,将当前物品放入背包中,并更新背包的最大价值table[i][j]=max(table[i-1][j-w[i]] + v[i] , table[i-1][j])
2.2.2、不放,当前物品的价值加上上一件物品除去当前物品重量的最大价值小于上一件物品的最大价值时,将当前物品放入背包中,并更新背包的最大价值table[i][j]=max(table[i-1][j-w[i]] + v[i] , table[i-1][j])
这里解释一下table[i-1][j-w[i]] + v[i],这里表示的情况是:将当前物品装入背包中v[i],然后将背包中的容量减掉当前物品的重量j-w[i],剩下的空间j-w[i]用来装上一件物品中计算的最大价值
下面来填表
第一步,当背包容量为0或物品数量为0时,背包的总价值为0
物品/容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(w=2,v=3) | 0 | ||||||||
2(w=3,v=4) | 0 | ||||||||
3(w=4,v=5) | 0 | ||||||||
4(w=5,v=6) | 0 |
第二步,判断当前物品是否能放入背包中,不能放入则直接继承当前位置上方的最大价值,能放入则判断两种情况的最大值
物品/容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(w=2,v=3) | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2(w=3,v=4) | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3(w=4,v=5) | 0 | ||||||||
4(w=5,v=6) | 0 |
这里解释一下table[2][2],物品2的重量为3,当背包容量为2时,当前物品的重量大于当前背包的容量,放不下,所以取上一件物品的最大价值,也就是table[1][2]的值。
table[2][5],当背包容量为5时,先将当前物品放入背包中,背包容量还剩5-3=2,在上一件物品中,当背包容量为2时的最大价值为3,所以这种情况的最大价值为3+4=7,与当前容量的上一件物品的最大价值table[1][5]=3相比较,取较大值7
接下来是一样的填法
物品/容量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(w=2,v=3) | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2(w=3,v=4) | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3(w=4,v=5) | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 |
4(w=5,v=6) | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 |
最后结果取table[4][8]=10
代码实现
二维数组实现
public class test {
public static void main(String[] args) {
//初始化物品
int[] w = {2,3,4,5,6};
int[] v = {3,4,5,6,7};
//背包最大容量
int MaxW = 8;
int maxV = getMaxV(w,v,MaxW);
System.out.println("背包的最大价值为:"+maxV);
}
//二维数组解决
private static int getMaxV(int[] w, int[] v, int maxW) {
int[][] table = new int[w.length][maxW + 1];
for (int i = 0;i<w.length;++i){
for (int j = 0;j<=maxW;++j){
if(i == 0 || j == 0){
//当背包容量为0或者物品数量为0时,最大价值为0
table[i][j] = 0;
}else if(w[i] <= j){
//当背包可以放入当前物品
table[i][j] = Math.max(table[i - 1][j],table[i - 1][j - w[i]] + v[i]);
}else {
//当背包不能放入当前物品
table[i][j] = table[i - 1][j];
}
}
}
return table[w.length - 1][maxW];
}
}
一维数组实现
public class test {
public static void main(String[] args) {
//初始化物品
int[] w = {2,3,4,5,6};
int[] v = {3,4,5,6,7};
//背包最大容量
int MaxW = 8;
int maxV = getMaxV(w,v,MaxW);
System.out.println("背包的组大价值为:"+maxV);
}
//一维数组解决
private static int getMaxV(int[] w, int[] v, int maxW) {
int[] arr = new int[maxW+1];
for (int i = 0;i<w.length;++i){
for (int j = maxW;j>=w[i];--j){
//用一个一维数组代替了二维数组的每一行,不断的更新这个数组,直到遍历完最后一件物品
arr[j] = Math.max(v[i] + arr[j - w[i]],arr[j]);
}
}
return arr[maxW];
}
}