先给一段代码熟悉一下最简单的背包
解法代码框架
import java.util.Scanner;
public class pack {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n;
//物品个数
n=scanner.nextInt();
int dv=scanner.nextInt();
//体积
int[] v=new int[n];
//价值
int[] w=new int[n];
for(int i=0;i<n;i++){
v[i]=scanner.nextInt();
}
for(int i=0;i<n;i++){
w[i]=scanner.nextInt();
}
int maxv[][]=new int[n+1][dv+1];
for(int i=0;i<=n;i++){
for(int j=0;j<=dv;j++){
if(i==0|| j==0){
maxv[i][j]=0;
continue;
}
//背包体积小于物品体积
if(j<v[i-1]) maxv[i][j]=maxv[i-1][j];
//背包体积大于等于物品体积
else {
//判断是否要将该物品添加进去,添加的话就要清除出该物品的体积的空位出来
int add=w[i-1]+maxv[i-1][j-v[i-1]];
int not=maxv[i][j]=maxv[i-1][j];
maxv[i][j]=add>not?add:not;
}
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=dv;j++) {
System.out.print(maxv[i][j]+" ");
}
System.out.println();
}
}
}
1—01背包
问题:m件物品,承重为n
1.我们会定义两个一维数组,一个是物体重量,一个是物体价值。
还有一个二维数组表示在i件物品和背包为j的情况下的最大价值,行为i=(m+1),列为j=(n+1)。i表示可以拿前面i件物品,j表示背包可装载容量
然后初始化在它只能装入0件物品的时候,各个承重的背包可以获得的价值。(第一行初始化为0价值)
2.然后做循环i,j
// 注意边界问题,i是从1开始的,j是从0开始的
// 因为F[i - 1][j]中i要减1
for (int i = 1; i <= N; i++) {
for (int j = 0; j <= V; j++) {
// 如果容量为j的背包放得下第i个物体
if (j >= weight[i]) {
F[i][j] = Math.max(F[i - 1][j - weight[i]] + value[i], F[i - 1][j]);
} else {
// 放不下,只能选择不放第i个物体
F[i][j] = F[i - 1][j];
}
}
}
2—01背包&&恰好完全装满
还有一种问题:要求背包恰好装满的情况。
在对二维数组初始化时:
1)v[0][0]初始化为0
2)第一行和第一列的其他值都设为-INF(未装满的状态)
原因:
1)占用空间为0的物品刚好可以装满空间为0的背包
2)只有上一层恰好装满时,使用状态方程得到的下一层才能是正好装满,因此初始值设为-INF,即上一层未装满时,下一层加入a[i]结果仍然是-INF即未装满。
同样的,初始化一维数组时,将v[0]设为0,其他设为-INF
3—完全背包
道理差不多,就是在加入新物体的过程中,再加一个循环来看看要加多少个,这里的增加是在二维数组中对该值进行修正。
//注意边界问题,i是从1开始的
for(int i = 1; i <= N; i++) {
//j是正序、降序没影响
for (int j = 0; j <= V; j++) {
for (int k = 0; k <= V / weight[i]; k++) {
if (j >= k * weight[i]) {
//注意:状态转移方程是F[i][j],而不是F[i - 1][j]
//因为这时放k个第i个物品,之后还可能继续放这个物体,所以应是F[i][j]
F[i][j] = Math.max(F[i - 1][j - k * weight[i]] + k * value[i], F[i][j]);
} else {
//可以省略,这里为什么不是F[i - 1][j]
//因为刚开始k=0,j >= 0 * weight[i]肯定成立,此时F[i][j] = F[i - 1][j]。
F[i][j] = F[i][j];
}
}
}
}
另一种写法,空间优化
//这里的做法是把原本的二维数组变成一个一维数组。
//但是原理一样
//就是得到Math.max((承载重量为j-weight[i]的最大价值+该物品价值),该承重的原本的价值)
//注意边界问题,i是从1开始的
for(int i = 1; i <= N; i++) {
//唯一不同的地方,j是正序遍历
for(int j = 0; j <= V; j++) {
if(j >= weight[i]) {
F[j] = Math.max(F[j - weight[i]] + value[i], F[j]);
}else {
//可以省略
F[j]= F[j];
}
}
}