01背包
public class 背包01 {
public static void main(String[] args) {
int[] weight = {0, 1, 4, 3};//物品的重量
int[] value = {0, 1500, 3000, 2000};//物品的单价
int c = 4;//书包的容量
dp(weight, value, c);
}
public static void dp(int[] weight, int[] value, int c) {
int n = value.length - 1;//物品的个数
//用于记录把物品放入书包的过程的表
//totalValue[i][j]表示放入前i个物品,书包剩余容量为j时,能放入的物品的最大值
int[][] totalValue = new int[n + 1][c + 1];//有n+1行,每行有c+1个元素
/*
0磅 1磅 2磅 3磅 4磅
0 0 0 0 0
吉他 0
音响 0
电脑 0
*/
int[][] result = new int[n + 1][c + 1];//用于记录放入的过程
for (int i = 1; i < n + 1; i++) {//从第一件物品开始放到最后一件物品
for (int j = 1; j < c + 1; j++) {//放某一件物品时,从背包剩余重量为1到剩余重量为4
if (weight[i] > j) {//如果放入的物品的重量大于背包剩余重量,则不能放入
totalValue[i][j] = totalValue[i - 1][j];//此时的总价值等于上一次放入物品时的总价值
} else {
if (totalValue[i - 1][j] < value[i] + totalValue[i - 1][j - weight[i]]) {
//totalValue[i - 1][j]:放入上一个物品时的总价值
//value[i-1]:当前物品的价值
//totalValue[i-1][j - weight[i-1]]:放入当前物品后,剩余空间能够放入物品的最大值(在上一行找)
totalValue[i][j] = value[i] + totalValue[i - 1][j - weight[i]];
result[i][j] = 1;//此时是最大价值的存放情况
} else {
totalValue[i][j] = totalValue[i - 1][j];
}
}
}
}
print(totalValue);
print(result);
System.out.println("背包的最大价值是" + totalValue[n][c]);
//totalValue[n][c]的物品放入情况就是最佳情况
int i = result.length - 1;
int j = result[0].length - 1;
while (i > 0 && j > 0) {
if (result[i][j] == 1) {
System.out.print("把第" + i + "个物品放入背包" + "\n");
j -= weight[i];//只有找到了,才需要改变背包的容量再继续找
}
i--;//不管找没找到,都需要退到前一行再继续找
}
}
public static void print(int[][] arr) {
for (int[] hang : arr) {
for (int i : hang) {
System.out.print(i + " ");
}
System.out.println();
}
}
01背包优化
public class 背包01优化 {
public static void main(String[] args) {
int[] weight = {0, 1, 4, 3};//物品的重量
int[] value = {0, 1500, 3000, 2000};//物品的单价
int c = 4;//书包的容量
youhua1(weight, value, c);
}
public static void youhua1(int[] weight, int[] value, int c) {
int n = value.length - 1;//物品的个数
int[] totalValue = new int[c + 1];//滚动数组,记录上一个物品放入的情况
int[] temp = new int[c + 1];//记录当前个物品放入的情况
for (int i = 1; i < n + 1; i++) {//从第一件物品开始放到最后一件物品
for (int j = 1; j < c + 1; j++) {//放某一件物品时,从背包剩余重量为1到剩余重量为4
if (weight[i] > j) {//如果放入的物品的重量大于背包剩余重量,则不能放入
temp[j] = totalValue[j];//此时的总价值等于上一次放入物品时的总价值
} else {
temp[j] = Math.max(totalValue[j], value[i] + totalValue[j - weight[i]]);
}
}
//每放完一次物品,就要把temp[]重新赋值给totalValue[]
for (int x = 0; x < temp.length; x++) {
totalValue[x] = temp[x];
}
System.out.println(Arrays.toString(totalValue));
}
System.out.println("背包的最大价值是" + totalValue[c]);
}
public static void youhua2(int[] weight, int[] value, int c) {
int n = value.length - 1;//物品的个数
int[] totalValue = new int[c + 1];//滚动数组,记录上一个物品放入的情况
for (int i = 1; i < n + 1; i++) {//从第一件物品开始放到最后一件物品
for (int j = c; j >= 1; j--) {//逆向遍历,防止数据被覆盖
if (weight[i] <= j) {
totalValue[j] = Math.max(totalValue[j], value[i] + totalValue[j - weight[i]]);
}
}
System.out.println(Arrays.toString(totalValue));
}
System.out.println("背包的最大价值是" + totalValue[c]);
}
}
01背包变型(逆向)
public class 背包01变型 {
public static void main(String[] args) {
int[] weight = {0, 1, 4, 3};//物品的重量
int[] value = {0, 1500, 3000, 2000};//物品的单价
int n = 3;//一共有3种物品
int c = 4;//至少装入的物品的总重量
//需求改为,至少要装够c重量的物品,要求物品的总价值最小
dp2(weight, value, n, c);
}
public static void dp(int[] weight, int[] value, int n, int c) {
int[][] sum = new int[n + 1][c + 1];
for (int x = 1; x < c + 1; x++) {//把第0行初始化为一个较大值
sum[0][x] = 0x3f3f3f3f;
}
for (int i = 1; i < n + 1; i++) {//从第一个物品开始
for (int j = 0; j < c + 1; j++) {//有可能至少装入的物品的重量为0
if (weight[i] >= j) {//如果当前物品的重量大于当前需要的重量,要么放且仅放当前物品,要么不放
sum[i][j] = Math.min(sum[i - 1][j], value[i]);
} else {//如果当前物品的重量小于当前需要的重量
sum[i][j] = Math.min(sum[i - 1][j], value[i] + sum[i - 1][j - weight[i]]);
}
}
}
System.out.println(sum[n][c]);
// for (int x = 0; x < sum.length; x++) {
// for (int y = 0; y < sum[x].length; y++) {
// System.out.print(sum[x][y] + "\t");
// }
// System.out.println();
// }
/*
w v 0 1 2 3 4
0 0 0 0x3f 0x3f 0x3f 0x3f
1 1500 0 1500 0x3f 0x3f 0x3f
4 3000 0 1500 3000 3000 3000
3 2000 0 1500 2000 2000 3000
*/
}
public static void dp2(int[] weight, int[] value, int n, int c) {
int[] sum = new int[c + 1];
for (int x = 1; x < c + 1; x++) {//初始化为一个较大值
sum[x] = 0x3f3f3f3f;
}
System.out.println(sum);
for (int i = 1; i < n + 1; i++) {//从第一个物品开始
for (int j = c; j >= 0; j--) {//有可能至少装入的物品的重量为0
if (weight[i] >= j) {//如果当前物品的重量大于当前需要的重量,要么放且仅放当前物品,要么不放
sum[j] = Math.min(sum[j], value[i]);
} else {//如果当前物品的重量小于当前需要的重量
sum[j] = Math.min(sum[j], value[i] + sum[j - weight[i]]);
}
}
System.out.println(Arrays.toString(sum));
}
System.out.println(sum[c]);
}
}
完全背包
每个物品的数量是无限的
public class 完全背包 {
public static void main(String[] args) {
int[] weight = {0, 1, 4, 3};//物品的重量
int[] value = {0, 1500, 3000, 2000};//物品的单价
int c = 4;//书包的容量
dp2(weight, value, c);
}
public static void dp(int[] weight, int[] value, int c) {
int n = value.length - 1;//物品的个数
int[] totalValue = new int[c + 1];//滚动数组,记录上一个物品放入的情况
for (int i = 1; i < n + 1; i++) {//从第一件物品开始放到最后一件物品
for (int j = c; j >= 1; j--) {//逆向遍历,防止数据被覆盖
for (int k = 0; k <= j / weight[i]; k++) {//从0个拿到当前容量下能拿的最大值
totalValue[j] = Math.max(totalValue[j], k * value[i] + totalValue[j - k * weight[i]]);
}
}
System.out.println(Arrays.toString(totalValue));
}
System.out.println("背包的最大价值是" + totalValue[c]);
}
public static void dp2(int[] weight, int[] value, int c) {
int n = value.length - 1;//物品的个数
int[] totalValue = new int[c + 1];//滚动数组,记录上一个物品放入的情况
for (int i = 1; i < n + 1; i++) {//从第一件物品开始放到最后一件物品
for (int j = weight[i]; j < c + 1; j++) {//从前往后遍历,因为要用到本行的数据
//从能放入物品的地方开始推
totalValue[j] = Math.max(totalValue[j], value[i] + totalValue[j - weight[i]]);
}
System.out.println(Arrays.toString(totalValue));
}
System.out.println("背包的最大价值是" + totalValue[c]);
}
}
多重背包
朴素的方法
public class 多重背包 {
public static void main(String[] args) {
int[] w = {0, 1, 4, 3};//物品的重量
int[] v = {0, 1500, 3000, 2000};//物品的单价
int[] s = {0, 5, 1500, 800};//每种物品的个数
int c = 10000;//背包容量
dp(w, v, s, c);
}
public static void dp(int[] w, int[] v, int[] s, int c) {
int n = w.length - 1;//有多少种物品
int[] sum = new int[c + 1];
for (int i = 1; i < n + 1; i++) {//从第一种物品开始装,到最后一种物品
for (int j = c; j >= 1; j--) {//从背包的最大容量开始,到容量为1
for (int k = 0; k <= s[i] && k * w[i] <= j; k++) {
sum[j] = Math.max(sum[j], k * v[i] + sum[j - k * w[i]]);
}
}
}
System.out.println(sum[c]);
}
优化的方法(二进制)
自己写的垃圾代码
public class 多重背包 {
public static void main(String[] args) {
int[] w = {0, 1, 4, 3};//物品的重量
int[] v = {0, 1500, 3000, 2000};//物品的单价
int[] s = {0, 5, 1500, 800};//每种物品的个数
int c = 10000;//背包容量
dp2(w, v, s, c);
}
//二进制优化
public static void dp2(int[] w, int[] v, int[] s, int c) {
int n = w.length - 1;//有多少种物品
int[] sum = new int[c + 1];
int n2 = 0;//一共要拆分成多少个物品
for (int x = 1; x < n + 1; x++) {
n2 += depart(s[x])[0];
}
int[] w2 = new int[n2 + 1];//转化为01背包后,初始化重量的数组
int[] v2 = new int[n2 + 1];//转化为01背包后,初始化单价的数组
int m = 0;//拆分系数
int count = 1;//记录新的数组的下标
for (int x = 1; x < n + 1; x++) {//遍历原先的数组
//计算第x个物品可以拆成多少个
int nx = depart(s[x])[0];
int y=0;
for (y = 0; y < nx-1; y++) {
//不管有没有余数,前面的都是一样的
m = (int) Math.pow(2, y);
w2[count] = m * w[x];
v2[count] = m * v[x];
count++;
}
//如果有余数,最后一个拆分系数不一样
if(depart(s[x])[1]!=0){
m=depart(s[x])[1];//有余数
}else{
m = (int) Math.pow(2, y);
}
w2[count] = m * w[x];
v2[count] = m * v[x];
count++;
}
for (int i = 1; i < n2 + 1; i++) {//从第一种物品开始装,到最后一种物品
for (int j = c; j >= 1; j--) {//从背包的最大容量开始,到容量为1
if (w2[i] <= j) {
sum[j] = Math.max(sum[j], v2[i] + sum[j - w2[i]]);
}
}
System.out.println(sum[c]);
}
//计算一个整数n可以拆成多少个2的整数幂的和,余数是多少
public static int[] depart(int n) {
int[] arr = new int[2];
int count = 0;
int i = 0;
while (i <= n) {
i += Math.pow(2, count);
count++;
}
if (i - Math.pow(2, count - 1) < n) {//有余数
arr[0] = count;//多少个数
arr[1] = n - i +(int) Math.pow(2, count - 1);//余数
} else {
arr[0]=count-1;//多少个数
arr[1]=0;//余数
}
return arr;
}
}
二维背包
public class 二维费用背包 {
public static void main(String[] args) {
int[] w = {0, 1, 4, 3};//物品的重量
int[] s = {0, 2, 5, 3};//物品的体积
int[] v = {0, 1500, 3000, 2000};//物品的单价
int n = 3;//物品的个数
int maxW = 4;//书包的最大重量容量
int maxS = 5;//背包的最大体积容量
int[][] sum = new int[maxW + 1][maxS + 1];
for (int x = 1; x < n + 1; x++) {//从第一件物品开始放到最后一件物品
for (int i = maxW; i >= 1; i--) {
for (int j = maxS; j >= 1; j--) {
if (w[x] <= i && s[x] <= j) {
sum[i][j] = Math.max(sum[i][j], v[x] + sum[i - w[x]][j - s[x]]);
}
}
}
}
System.out.println("背包的最大价值是" + sum[maxW][maxS]);
}
}
二维费用背包变型
public class 二维费用背包变型 {
public static void main(String[] args) {
int[] O = {0, 3, 10, 5, 1, 4};//每个气缸的氧气含量
int[] N = {0, 36, 25, 50, 45, 20};//每个气缸的氮气含量
int[] W = {0, 120, 129, 250, 130, 119};//每个气缸的重量
int n = 5;//共有五种气缸
int needO = 5;//需要的氧气
int needN = 60;//需要的氮气
dp2(O, N, W, n, needO, needN);
}
public static void dp(int[] O, int[] N, int[] W, int n, int needO, int needN) {
int[][] sum = new int[needO + 1][needN + 1];//还需要i升氧气和j升氮气的情况下,气缸最小的总重量
for (int x = 1; x < needO + 1; x++) {
for (int y = 1; y < needN + 1; y++) {
sum[x][y] = 0x3f3f3f3f;
}
}//初始化
for (int x = 1; x < n + 1; x++) {//考虑带第1个气缸,第2个气缸,...,第n个气缸
for (int i = needO; i >= 0; i--) {//有可能氧气的需求是0
for (int j = needN; j >= 0; j--) {//有可能氮气的需求是0
if (O[x] >= i && N[x] >= j) {//如果当前的氧气和氮气的含量,比还需要的氧气和氮气大
sum[i][j] = Math.min(sum[i][j], W[x]);//如果不拿就是上一轮的气缸重量,如果拿就是当前气缸的重量
} else if (O[x] < i && N[x] < j) {//如果当前的氧气和氮气的含量,都比还需要的氧气和氮气小
sum[i][j] = Math.min(sum[i][j], W[x] + sum[i - O[x]][j - N[x]]);
} else if (O[x] >= i && N[x] < j) {//如果当前的氧气含量大于需求,氮气含量小于需求
sum[i][j] = Math.min(sum[i][j], W[x] + sum[i][j - N[x]]);//如果拿,氧气已经满足条件,只需要氮气满足条件
} else if (O[x] < i && N[x] >= j) {//如果当前的氮气含量大于需求,氧气含量小于需求
sum[i][j] = Math.min(sum[i][j], W[x] + sum[i - O[x]][j]);//如果拿,氮气已经满足条件,只需要氧气满足条件
}
}
}
}
int min = sum[needO][needN];
System.out.println(min);
}
public static void dp2(int[] O, int[] N, int[] W, int n, int needO, int needN) {
int[][] sum = new int[needO + 1][needN + 1];//还需要i升氧气和j升氮气的情况下,气缸最小的总重量
for (int x = 1; x < needO + 1; x++) {
for (int y = 1; y < needN + 1; y++) {
sum[x][y] = 0x3f3f3f3f;
}
}//初始化
for (int x = 1; x < n + 1; x++) {//考虑带第1个气缸,第2个气缸,...,第n个气缸
for (int i = needO; i >= 0; i--) {//有可能氧气的需求是0
for (int j = needN; j >= 0; j--) {//有可能氮气的需求是0
if (O[x] >= i && N[x] >= j) {//如果当前的氧气和氮气的含量,比还需要的氧气和氮气大
sum[i][j] = Math.min(sum[i][j], W[x]);//如果不拿就是上一轮的气缸重量,如果拿就是当前气缸的重量
} else {
int k = i - O[x] >= 0 ? i - O[x] : i;
int m = j - N[x] >= 0 ? j - N[x] : j;
sum[i][j] = Math.min(sum[i][j], W[x] + sum[k][m]);
}
}
}
}
int min = sum[needO][needN];
System.out.println(min);
}
}