#问题背景
背包问题:有一个背包,容量为4磅,现有如下物品
物品 | 重量 | 价格 |
---|---|---|
吉他 | 1 | 1500 |
音响 | 4 | 3000 |
电脑 | 3 | 2000 |
目标:
1)要求达到的目标为装入的背包的总价值最大,并且重量不超出背包的容量
2)要求装入的物品不能重复
思路分析
物品重量 :
w
[
i
]
=
1
,
4
,
3
w[i] = {1,4,3}
w[i]=1,4,3
物品价值:
v
a
l
[
i
]
=
1500
,
3000
,
2000
val[i]={1500,3000,2000}
val[i]=1500,3000,2000
0磅 | 1磅 | 2磅 | 3磅 | 4磅 | |
---|---|---|---|---|---|
0物 | 0 | 0 | 0 | 0 | 0 |
1号(1磅,1500) | 0 | 1500 | 1500 | 1500 | 1500 |
2号(4磅,3000) | 0 | 1500 | 1500 | 1500 | 3000 |
3号(3磅,2000) | 0 | 1500 | 1500 | 2000 | 3500 |
由图可知,最大的路径是存放1号和3号
设价值矩阵为
v
[
i
]
[
j
]
,
v
[
i
]
[
j
]
v[i][j],v[i][j]
v[i][j],v[i][j]表示在背包容量为j的情况下,放第i个物品时的最大价值
如果
w
[
i
]
<
j
,
v
[
i
]
[
j
]
=
v
[
i
−
1
]
[
j
]
w[i]<j,v[i][j] = v[i-1][j]
w[i]<j,v[i][j]=v[i−1][j]
否则
w
[
i
]
>
=
j
,
v
[
i
]
[
j
]
=
m
a
x
(
v
a
l
[
i
]
+
v
[
i
−
1
]
[
j
−
w
[
i
]
]
,
v
[
i
−
1
]
[
j
]
)
w[i]>=j,v[i][j] = max(val[i]+v[i-1][j-w[i]],v[i-1][j])
w[i]>=j,v[i][j]=max(val[i]+v[i−1][j−w[i]],v[i−1][j])
如果当前物品的重量
>
>
>背包的重量,则当前物品放不下,此时最大价值和前一个物品的最大价值相同,即
v
[
i
]
[
j
]
=
v
[
i
−
1
]
[
j
]
v[i][j]=v[i-1][j]
v[i][j]=v[i−1][j]。
如果当前物品的重量
<
=
<=
<=背包的重量,则当前物品可以放下。所以可以放该物品也可以不放,
如果不放该物品,此时的价值与前一个物品的价值相同为
v
[
i
−
1
]
[
j
]
v[i-1][j]
v[i−1][j]
如果放该物品,此时的价值为当前的物品价值+背包容量减去当前物品的重量的情况下的能放下前
i
−
1
i-1
i−1个物品的最大值,即
v
a
l
[
i
]
+
v
[
i
−
1
]
[
j
−
w
[
i
]
]
val[i]+v[i-1][j-w[i]]
val[i]+v[i−1][j−w[i]].
所以,应该取两者之间的较大值
w
[
i
]
>
=
j
,
v
[
i
]
[
j
]
=
m
a
x
(
v
a
l
[
i
]
+
v
[
i
]
[
j
−
w
[
i
]
]
,
v
[
i
−
1
]
[
j
]
)
w[i]>=j,v[i][j] = max(val[i]+v[i][j-w[i]],v[i-1][j])
w[i]>=j,v[i][j]=max(val[i]+v[i][j−w[i]],v[i−1][j])
java 代码
// An highlighted block
package dynamic;
public class knapsackProblem {
public static void main(String[] args) {
// TODO
int[] w = {1, 4, 3};//物品的重量
int[] val = {1500,3000,2000};//物品的价值
int m =4;//背包的容量
int n =val.length;//物品的个数
//创建二维数组:
// v[i]v[j] 表示前i个物品中能够装入容量为j的背包中的最大价值
int[][] v = new int[n+1][m+1];
//为了记录放入商品的情况,我们定义一个二维数组
int[][] path = new int[n+1][m+1];
//初始化第一行和第一列,这里在本程序中,可以不去处理,默认是0
for(int i=0;i<v.length;i++){
v[i][0] = 0;//这里将第一列设置为0,包的容量为0时,包的价值都是0
}
for(int i=0;i<v[0].length;i++){
v[0][i] = 0;//将第一行设置0,物品的数量为0时,包的价值都是0
}
//根据前面得到公式来动态规划处理
for(int i=1;i<v.length;i++){ //不处理第一行,i是从1开始的
for(int j=1;j<v[0].length;j++){ //不处理第一列,j是从1开始的
//公式
if(w[i-1] >j){ //因为我们的程序是从1开始的,因为原来公式的w[i]修改成w[i-1]
v[i][j]= v[i-1][j];
}else{
//说明:
//因为我们的i从1开始的,因此公式需要调整
//v[i][j]
//为了记录商品存放到背包的情况,我们需要用if-else来体现公式
if(v[i-1][j] < val[i-1]+v[i-1][j-w[i-1]]){
v[i][j] = val[i-1]+v[i-1][j-w[i-1]];
//把当前的情况记录到path
path[i][j] = 1;
}else{
v[i][j] = v[i-1][j];
}
}
}
}
//
int i =path.length -1; //行的最大下标
int j = path[0].length-1; //列的最大下标
while(i>0 && j>0){ //从path的最后开始找
if(path[i][j] ==1){
System.out.printf("第%d个商品放入到背包\n",i);
j -=w[i-1];
}
i--;
}
//输出一下v,看看当前的情况
for(int l=0;l<v.length;l++){
for(int k=0;k<v[l].length;k++){
System.out.print(v[l][k] + " ");
}
System.out.println();
}
}
}