背包问题主要分为0-1背包、完全背包、多重背包和分组背包问题。
0-1 背包
问题描述:有一个容量为bagsize的背包,要从若干个重量为weight[i]、价值为value[i]物品中选取,使其总价值最大。
二维数组与一维数组遍历顺序不同的原因
二维数组
package TEST;
import java.util.Scanner;
//0-1背包问题二维数组
class test{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String[] str=sc.nextLine().split(" ");
int bagsize=Integer.parseInt(str[1]);
String[] sweight=sc.nextLine().split(" ");
int[] weight=new int[sweight.length];
for(int i=0;i<weight.length;i++){
weight[i]=Integer.parseInt(sweight[i]);
}
String[] svalue=sc.nextLine().split(" ");
int[] value=new int[svalue.length];
for(int i=0;i<value.length;i++){
value[i]=Integer.parseInt(svalue[i]);
}
System.out.println(testWeightBagProblem(weight,value,bagsize));
}
public static int testWeightBagProblem(int[] weight,int[] value,int bagsize){
//1.含义dp[i][j]意味着0到i的背包随意选择满足容量为j最大的价值
//2.递推公式:dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
//3.初始化dp[i][0]=0(容量为0时价值为0),dp[0][小于weight[0]]=0,dp[0][大于等于weight[0]]=weight[i];
//4.遍历顺序层数 既可以先遍历背包数,后遍历重量;也可以先遍历重量,后遍历背包数
int[][] dp=new int[weight.length][bagsize+1];
//因为数组初始化默认是0,故可以不写
// for(int i=0;i<weight.length;i++){
// dp[i][0]=0;
// }
// for(int j=0;j<weight[0];j++){
// dp[0][j]=0;
// }
for(int j=weight[0];j<bagsize+1;j++){
dp[0][j]=value[0];
}
for(int i=1;i<dp.length;i++){
for(int j=1;j<dp[i].length;j++){
if(j-weight[i]<0){
//当前当前背包容量不能放i,最大值等于dp[i-1][j]
dp[i][j]=dp[i-1][j];
}else{
//当前当前背包容量可以放i,最大值在放与不放之间去最大值
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
}
return dp[weight.length-1][bagsize];
}
}
一维数组
package TEST;
import java.util.Scanner;
//0-1背包问题一维数组
class test2{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String[] str=sc.nextLine().split(" ");
int bagsize=Integer.parseInt(str[1]);
String[] sweight=sc.nextLine().split(" ");
int[] weight=new int[sweight.length];
for(int i=0;i<weight.length;i++){
weight[i]=Integer.parseInt(sweight[i]);
}
String[] svalue=sc.nextLine().split(" ");
int[] value=new int[svalue.length];
for(int i=0;i<value.length;i++){
value[i]=Integer.parseInt(svalue[i]);
}
System.out.println(testWeightBagProblem(weight,value,bagsize));
}
public static int testWeightBagProblem(int[] weight,int[] value,int bagsize){
//1.含义dp[j]意味着0到i的背包随意选择满足容量为j最大的价值
//2.递推公式:dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i])
//3.初始化dp[j]=0;
//4.遍历顺序层数 只可以先遍历背包数,后遍历重量,而且遍历重量从后往前遍历,防止重复选入背包
int[] dp=new int[bagsize+1];
for(int i=0;i<weight.length;i++){
//遍历顺序从后往前,避免重复选,如果从前往后则是完全背包问题
for(int j=bagsize;j>=0;j--){
if(j-weight[i]<0){
System.out.print(dp[j]+" ");
}else{
dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i]);
System.out.print(dp[j]+" ");
}
System.out.println();
}
}
return dp[bagsize];
}
}
完全背包
与0-1背包不同的地方在于既可以先遍历物品后遍历背包,也可以先遍历背包后遍历物品,对于i,j都是从前向后遍历的
组合:先遍历物品后遍历背包,例题:518. 零钱兑换 II - 力扣(LeetCode)
排列:先遍历背包后遍历物品,例题:377. 组合总和 Ⅳ - 力扣(LeetCode)
多重背包
在01背包基础上,多循环一次背包个数。
package TEST;
//多重背包问题
class test{
public static void main(String[] args){
//dp[j]表示容量为i情况下,背包所能放的最大价值
//递推公式dp[j]=Math.max(dp[j],dp[j-k*weight[i]]+k*value[i]);
//初始化dp[0]=0
//先物品后背包,背包从后往前遍历,加一次物品个数的循环
int[] weight = new int[] {1, 3, 4};
int[] value = new int[] {15, 20, 30};
int[] nums = new int[] {2, 3, 2};
int bagWeight = 10;
int[] dp=new int[bagWeight+1];
for(int i=0;i<weight.length;i++){ //先遍历物品
for(int j=bagWeight;j>weight[i];j--){ //后遍历背包
for(int k=1;k<=nums[i]&&j>=k*weight[i];k++){ //再遍历物品个数,从1开始到nums[i]结束
dp[j]=Math.max(dp[j],dp[j-k*weight[i]]+k*value[i]);
}
}
}
System.out.println(dp[bagWeight]);
}
}