有一个背包,容量为一定值,现有不同可供选择的物品,每个物品有自己的重量属性和价值属性,要求:
- 目标为总价值最大,且重量不超出
- 装进背包的物品不重复
背包问题可分为01背包和完全背包(完全背包指的是每件物品都无限件可用。
思路分析:利用动态规划解决,每次遍历,对于给定的n个物品,设v[i]、w[i]分别为第i个物品的价值和容量,c为背包容量。
吉他 一磅 1500
音响 四磅 3000
电脑 三磅 2000
物品 | 0磅 | 1磅 | 2磅 | 3磅 | 4磅 |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | |
吉他 | 0 | 1500 | 1500 | 1500 | 1500 |
音响 | 0 | 1500 | 1500 | 1500 | 3000 |
电脑 | 0 | 1500 | 1500 | 2000 | 3500 |
- v[i][0] = v[0][j]=0 表中的第一行和第一列都等于0
- 当w[i]>j的时候,也就是超出背包的容量了,所以要取上一次背包的容量。v[i][j] = v[i-1][j];
- 当j>w[i]时,v[i][j] = max(v[i-1][j],v[i-1][j-w[i]] + v[i]),比较上一次背包能够装的value和背包总容量减去这一次装入的物品重量能够装入的最大值加上这次装入物品的value,取较大值。
以下代码为不需要记录背包存放情况而只需要value时的情况。
public class TestKnapsack {
@Test
public void test() {
int []w = {1,4,3};//物品重量
int []value = {1500,3000,2000};//物品的价值
int m = 4;//背包容量
int n = value.length;//物品的个数
//创建二维数组=表
//v[i][j]表示在前i个物品中能够装入j容量的最大值
int [][]v = new int[n+1][m+1];
//因为Java特性是已经把每个值赋值为0,则不用手动将第一行第一列设置为0
for(int i = 1;i < n+1;i++) {
for(int j = 1;j < m+1;j++) {
if(w[i-1] > j ) {//因为从1开始,所以w数组下标要-1
v[i][j] = v[i-1][j];
} else {
v[i][j] = Math.max(v[i-1][j],v[i-1][j-w[i-1]]+value[i-1]);//同理,value数组下标-1
}
}
}
System.out.println("在背包中能装入价值最大为" + v[3][4]);
}
}
如果我们需要记录背包存放物品的情况,则需要再定义一个path的二维数组。
public class TestKnapsack {
@Test
public void test() {
int []w = {1,4,3};//物品重量
int []value = {1500,3000,2000};//物品的价值
int m = 4;//背包容量
int n = value.length;//物品的个数
int [][]path = new int [n+1][m+1];
//创建二维数组=表
//v[i][j]表示在前i个物品中能够装入j容量的最大值
int [][]v = new int[n+1][m+1];
//因为Java特性是已经把每个值赋值为0,则不用手动将第一行第一列设置为0
for(int i = 1;i < n+1;i++) {
for(int j = 1;j < m+1;j++) {
if(w[i-1] > j ) {//因为从1开始,所以w数组下标要-1
v[i][j] = v[i-1][j];
} else {
//与之前代码不同的地方
v[i][j] = Math.max(v[i - 1][j], v[i - 1][j - w[i - 1]] + value[i - 1]);//同理,value数组下标-1
if(v[i-1][j] < v[i-1][j-w[i-1]]+value[i-1]) {
path[i][j] = 1;
}
}
}
}
//System.out.println("在背包中能装入价值最大为" + v[3][4]);
int j = path[0].length-1;
int i = path.length-1;
while(i >0 && j>0) {
if(path[i][j] == 1) {
System.out.printf("第%d个物品被放入背包",i);
j-=w[i-1];
}
i--;
}
}
}