前言
0,1背包问题满足下列条件:
问题分析:
一、减少规模
- 定义m(i, j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优 值
- m(i+1, j)为可选择物品为i+1,…,n时0-1背包问题的最优值
- …
- m(n, j)为可选择物品为n时0-1背包问题的最优值,此时,规模已为1
二、推导递归式
判断是否放入第i件?
- 不放,背包当前产生价值仍为m(i+1,j)
- 放入,调整背包容量j-wi,背包当前产生价值为m(i+1, j-wi)+vi
实例分析
01背包问题dp算法从第n行开始填表,n=5,即第五行,用上面递归式的第二个
- 当背包容量( j )小于第五个物品的重量(Wn=W5)时,不放入。
- 当背包容量( j )大于等于第五个物品重量(Wn=W5)时,放入。
- 需要注意第五个物品的重量可能比背包总容量c都要大,在实现代码时需要考虑进去。
第四行
- 背包容量为0-4时,即m(4,0),m(4,1),m(4,2),m(4,3) ,m(4,4),第四个物品的重量为5,第四个物品根本放不进去,所以按照递归式 ,m(4,0~4)=m(5,0-4)。
-
当背包容量大于等于物品四的容量时,我们就要考虑是否要放入物品四。是否放入,取决于放入与不放入两种情况的价值大小。如果放入物品四后总价值小于不放入物品四的总价值则不放,反之则放。
-
例如背包容量为5时,不放入物品四:总价值(Value1)=背包容量为5时放入物品五时的价值。放入物品四:总价值(Value2)=物品四的价值+背包容量为0(5-5)时放入物品五时的价值。m(4,5) = max(Value1,Value2)。而放入物品五时的价值在不同背包容量时已经在前面计算出来了。背包容量为5时总价值计算如下:
-
背包容量为9时,不放入物品四:总价值(Value1)=背包容量为9时放入物品五时的价值。放入物品四:总价值(Value2)=物品四的价值+背包容量为4(9-5)时放入物品五时的价值。m(4,9) = max(Value1,Value2)。背包容量为5时总价值计算如下:
第三行 -
因为物品三的重量为6,所以当背包容量为0-5时,无法放入物品三。m(3,0~5)=m(4,0-5)
-
背包容量为6时,不放入物品三:总价值(Value1)=背包容量为6时放入物品四和五时的价值。放入物品三:总价值(Value2)=物品三的价值+背包容量为0(6-6)时放入物品四和五时的价值。m(3,6) = max(Value1,Value2)。
-
背包容量为10时,不放入物品三:总价值(Value1)=背包容量为10时放入物品四和五时的价值。放入物品三:总价值(Value2)=物品三的价值+背包容量为4(10-6)时放入物品四和五时的价值。m(3,10) = max(Value1,Value2)。
其他行一样
实现
一、JAVA代码实现
import java.util.Scanner;
public class zero_oneBackPack{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入物品的数量:");
int n = sc.nextInt();//物品数量
System.out.println("输入背包容量:");
int c = sc.nextInt();//背包容量
int[] v = new int[n+1];//各物品的价值
int[] w = new int[n+1];//各物品的重量
System.out.println("输入各物品的价值:");
for (int i = 1; i <= n; i++) {
v[i] = sc.nextInt();
}
System.out.println("输入各物品的重量:");
for (int i = 1; i <= n; i++) {
w[i] = sc.nextInt();
}
int[][] cc = new int[n+1][c+1];
for (int i =1; i <=c; i++) {//填写第n行
if(i>=w[n]) {
cc[n][i] = v[n];
}else
cc[n][i] = 0;
}
for (int i = n-1; i>=1; i--) {//填写1~(n-1)行
for (int j = 1; j <=c; j++) {
if(j>=w[i]) {//背包容量大于等于第i行物品的重量时比较两种情况的总价值
cc[i][j] = Math.max(cc[i+1][j], cc[i+1][j-w[i]]+v[i]);
}else //背包容量小于第i行物品的重量
cc[i][j] = cc[i+1][j];
}
}
System.out.println("总价值计算情况:");
for (int i = 1; i <=n; i++) {
System.out.print("第"+(i)+"行:\t");
for (int j = 1; j <=c; j++) {
System.out.print(cc[i][j]+"\t");
}
System.out.println();
}
}
}
二、结果
三、构造物品的选取
我们在填表的时候,如果第i个物品不选取,则选取第i+1到n个物品,即m(i+1,1~n)。看上面的第三行和第四行,蓝色区域,m(3,6)=m(4,6),表示第三个物品没有选取。
构造:
void traceback(int c,int[][] cc){
int[] a = new int[n];
int j=c;
for (int i = 0; i <n-1; i++) {//判断1-(n-1)个物品是否选取
if(cc[i+1][j]==cc[i+2][j]) {
a[i]=0;//相等则没有选取,对于位填0
}else {
a[i]=1;//不相等则没有选取,对于位填1
j=j-w[i+1];//并且,减去相应的重量
}
}
if(cc[n][j]==0) {//判第n个物品是否选取
a[n-1]=0;
}else
a[n-1] = 1;
System.out.println("选取的哪些物品:");
System.out.print("选取了:第"+"\t");
for (int i = 0; i < a.length; i++) {
if(a[i]==1)
System.out.print(i+1+"\t");
}
}
结果: