完全背包来了,嘿嘿~
一.完全背包转01背包_暴力法I
此方法就是枚举当前物品的数量,直至物品体积 * 数量 > 背包体积。
import java.util.Scanner;
public class FullBackpack完全背包转01背包_暴力法I {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int[] value = new int[N];
int[] weight = new int[N];
for (int i = 0; i < N; i++) {
weight[i] = input.nextInt();
value[i] = input.nextInt();
}
int[] backpacks = new int[V + 1];
for (int i = 0; i < N; i++) {
for (int j = V; j >= weight[i]; j--) {
for (int k = 1; k <= j / weight[i]; k++) {
backpacks[j] = Math.max(backpacks[j], backpacks[j - k * weight[i]] + k * value[i]);
}
}
}
System.out.println(backpacks[V]);
}
}
二.完全背包转01背包_暴力法II
此方法是拆分,我们求出背包最多可以装多少当前物品:当前物品数量 = 背包体积 / 当前物品体积(向下取整),然后将物品分为当前物品数量个相同物品。
import java.util.ArrayList;
import java.util.Scanner;
/**
* 一个物体能用多次(这个多次所限的范围是背包大小范围内),所以这个多次是可以求出来的,也就是size = V/weight[i],所以我们可以将这件物品分为size件物品。
* 此方法只是让人更加容易解题(比如只理解了01背包的小伙伴),实际上时间复杂度没有任何的改变。
*/
public class FullBackpack完全背包转01背包_暴力法II {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int temporaryValue = 0;
int temporaryWeight = 0;
ArrayList<Integer> value = new ArrayList<>();
ArrayList<Integer> weight = new ArrayList<>();
for (int i = 0; i < N; i++) {
temporaryWeight = input.nextInt();
temporaryValue = input.nextInt();
for (int j = 0; j < V / temporaryWeight; j++) {
weight.add(temporaryWeight);
value.add(temporaryValue);
}
}
N = value.size();
int[] backpacks = new int[V + 1];
for (int i = 0; i < N; i++) {
for (int j = V; j >= weight.get(i); j--) {
backpacks[j] = Math.max(backpacks[j], backpacks[j - weight.get(i)] + value.get(i));
}
}
System.out.println(backpacks[V]);
}
}
三.完全背包转01背包_二进制解法
我们为了减小数据量,所以采用了二进制解法,为什么选择二进制解法?我们可以将上一个做法看作时这样的:
假设物品数量 = 背包体积 / 当前物品体积(向下取整)= 7,则相当于分解为7个1,然后互相结合。采用二进制也可以达到相同的效果:分解为1,2,4(7(10) = 111(2),1(10) = 001(2),2(10) = 010(2),4(10) = 100(2)),为什么分解为三个数字?
0(什么也不选)
1(选1)
2(选2)
3(选1,2)
4(选4)
5(选1,4)
6(选2,4)
7(选1,2,4)
分解后的二进制数字,可以组合成任意小于等于物品数量的件数。
7这个数字正好符合2^k - 1的形式,那么例如13这种呢?需要分解为4个数字。
如果我们分解为:1,2,4,8,那么组合的区间是[0, 15],15是大于13的,这使我们不能接受!!!
如果我们分解为:1,2,4,13 - (1 + 2 + 4) = 6,那么组合的区间就变成了[0, 13],满足题目的需要。
import java.util.ArrayList;
import java.util.Scanner;
public class FullBackpack完全背包转01背包_二进制解法 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int temporaryValue = 0;
int temporaryWeight = 0;
ArrayList<Integer> value = new ArrayList<>();
ArrayList<Integer> weight = new ArrayList<>();
for (int i = 0; i < N; i++) {
temporaryWeight = input.nextInt();
temporaryValue = input.nextInt();
int size = V / temporaryWeight;
for (int j = 1; j <= size; j <<= 1) {
size -= j;
weight.add(j * temporaryWeight);
value.add(j * temporaryValue);
}
if (size > 0) {
weight.add(size * temporaryWeight);
value.add(size * temporaryValue);
}
}
N = value.size();
int[] backpacks = new int[V + 1];
for (int i = 0; i < N; i++) {
for (int j = V; j >= weight.get(i); j--) {
backpacks[j] = Math.max(backpacks[j], backpacks[j - weight.get(i)] + value.get(i));
}
}
System.out.println(backpacks[V]);
}
}
四.完全背包_最大值
import java.util.Scanner;
/**
* 此算法是求出背包的物品的最大总价值(物品可选多次,包可以装不满),如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] <= weight[j]
* 且value[i] >= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最大的物品),减少数据量。
*/
public class FullBackpack完全背包_最大值 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int[] value = new int[N];
int[] weight = new int[N];
for (int i = 0; i < N; i++) {
weight[i] = input.nextInt();
value[i] = input.nextInt();
}
int[] backpacks = new int[V + 1];
/**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
for (int i = 0; i < N; i++) {
for (int j = weight[i]; j <= V; j++) {
backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);
}
}
System.out.println(backpacks[V]);
}
}
五.完全背包_包满最大值
import java.util.Arrays;
import java.util.Scanner;
/**
* 此算法是求出当满足装满背包的物品的最大总价值,如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] <= weight[j]
* 且value[i] >= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最大的物品),减少数据量(求出当满足装满背包的物品的最大总价值!!!)。
*/
public class FullBackpack完全背包_包满最大值 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int[] value = new int[N];
int[] weight = new int[N];
for (int i = 0; i < N; i++) {
weight[i] = input.nextInt();
value[i] = input.nextInt();
}
int[] backpacks = new int[V + 1];
/**如果赋值为Integer.MIN_VALUE,则不需要进行if (backpacks[j - weight[i]] != Integer.MIN_VALUE),这是因为
* 在进行对比时,backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);会有一个
* backpacks[j - weight[i]] + value[i],此值永远是负值(前提是此值足够小于求得背包里物品的价值,最大值不会大于背包里物品价值的最小值,
* 也就是0),与预期符合,如果使用一个比较小的值,比如-1,如果不进行if (backpacks[j - weight[i]] != -1),
* 造成backpacks[j - weight[i]] + value[i]值有可能大于backpacks[j],此时结果不符合预期,所以不满足if条件,不参与运算。*/
Arrays.fill(backpacks, 1, V + 1, -1);
/**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
for (int i = 0; i < N; i++) {
for (int j = weight[i]; j <= V; j++) {
if (backpacks[j - weight[i]] != -1)
backpacks[j] = Math.max(backpacks[j], backpacks[j - weight[i]] + value[i]);
}
}
if (backpacks[V] == -1)
System.out.println("Backpack is not full");
else
System.out.println(backpacks[V]);
}
}
六.完全背包_包满最小值
import java.util.Arrays;
import java.util.Scanner;
/**
* 此算法是求出当满足装满背包的物品的最小总价值,如果针对大量的数据,我们需要对数据做一些预处理,首先将weight[x] > V的物品清除,再假设i,j两个物品满足weight[i] >= weight[j]
* 且value[i] <= value[j],将第j件物品剔除掉(也可以理解为在两物品的体积相同的情况下,保留价值最小的物品),减少数据量(求出当满足装满背包的物品的最小总价值!!!)。
*/
public class FullBackpack完全背包_包满最小值 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int N = input.nextInt();
int V = input.nextInt();
int[] value = new int[N];
int[] weight = new int[N];
for (int i = 0; i < N; i++) {
weight[i] = input.nextInt();
value[i] = input.nextInt();
}
int[] backpacks = new int[V + 1];
/**如果赋值为Integer.MAX_VALUE,则需要进行if (backpacks[j - weight[i]] != Integer.MAX_VALUE),这是因为
* 在进行对比时,backpacks[j] = Math.min(backpacks[j], backpacks[j - weight[i]] + value[i]);会有一个
* backpacks[j - weight[i]] + value[i],会造成溢出,成为负值,与预期不符合,如果使用一个比较大的值,比如1000000
* (前提是此值足够大于求得背包里物品的价值),这样就不会溢出,结果符合预期*/
Arrays.fill(backpacks, 1, V + 1, Integer.MAX_VALUE);
/**为什么这就代表一个物体能用多次(这个多次所限的范围是背包大小范围内),这是因为每次使用的都是以前的当前物品值所产生的值,相当于使用了多次*/
for (int i = 0; i < N; i++) {
for (int j = weight[i]; j <= V ; j++) {
if (backpacks[j - weight[i]] != Integer.MAX_VALUE)
backpacks[j] = Math.min(backpacks[j], backpacks[j - weight[i]] + value[i]);
}
}
if (backpacks[V] == Integer.MAX_VALUE)
System.out.println("Backpack is not full");
else
System.out.println(backpacks[V]);
}
}
七.完全背包_最小值
public class FullBackpack完全背包_最小值 {
public static void main(String[] args) {
/** 为什么不写完全背包或者01背包求最小值的代码?你是不是傻?又没有说要装满,也就是装不装物品都可以!!!,
* 求最小值,那肯定不装物品时背包的值为最小值,也就是0,啊哈哈哈哈*/
}
}
如果文中我的理解有偏差或者错误,请阅读者评论指出,不胜感激。