问题描述:
一个背包的总容量为V,现在有N类物品,第i类物品的重量为weight[i],价值为value[i]
那么往该背包里装东西,怎样装才能使得最终包内物品的总价值最大。这里装物品主要由三种装法:
1、0-1背包:每类物品最多只能装一次
2、多重背包:每类物品都有个数限制,第i类物品最多可以装num[i]次
3、完全背包:每类物品可以无限次装进包内
一、01背包问题
1.二维数组方法
/**
* 0-1背包--二维数组方法
* 状态转移方程为
*
* dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >= w[i]
*
*
* @param V 背包总容量(重量)
* @param N 物品个数
* @param weight 物品重量数组
* @param value 物品价值数组
* @return
*/
public static int zeroOnePack(int V, int N, int[] weight, int[] value){
int[][] dp = new int[N+1][V+1];
//下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
for(int i=1; i0; i--){
if(dp[i][j] > dp[i-1][j]){
System.out.print(i + ", ");
j = j-weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
System.out.println("zeroOnePack maxValue: " + dp[N][V]);
return dp[N][V];
}
2.一维数组方法
/**
* 0-1背包--一维数组方法
* 当背包容量为j时,已经放入了i件物品,最大价值为d[j]
* 当第i件物品比背包容量还大时,d[j]=d[j];
* 当第i件物品比背包容量小时,就要判断两种情况:不放的话,和上式相同,放的话,背包容量-第i件物品的质量再去装前i-1件物品,
* 所得得最大价值加上第i件物品的价值,两者较大值即为最优解。d[j]=max(dp[j],dp[j-w[i]]+v[i])
*
* 状态转移方程为:
* dp[j] = Math.max(dp[j-weight[i-1]]+value[i-1],dp[j]);
*
*
* @param V 背包总容量(重量)
* @param N 物品个数
* @param weight 物品重量数组
* @param value 物品价值数组
* @return
*/
public static int zeroOnePack_02(int V, int N, int[] weight, int[] value){
int[] dp = new int[V+1];
for(int i=1; i=weight[i-1]; j--){
//由于weight和value数组下标都是从0开始,注意第i个物品的重量为weight[i-1],价值为value[i-1]
dp[j] = Math.max(dp[j], dp[j-weight[i-1]] + value[i-1]);
System.out.print(dp[j] + ", ");
}
System.out.println("--------");
}
//打印所有背包内的物品编号
int j = V;
for(int i=N; i >0; i--){
if(dp[j] < dp[j-weight[i-1]] + value[i-1]){
System.out.print(i + ", ");
j = j-weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
System.out.println("zeroOnePack_02 maxValue: " + dp[V]);
return dp[V];
}
二、多重背包
1.二维数组方法
/**
* 多重背包
* k为装入第i种物品的件数, k <= min(n[i], j/w[i])
* dp[i][j] = max{(dp[i-1][j − k*w[i]] + k*v[i]) for every k}
*
*
* @param V 背包总容量(重量)
* @param N 物品个数
* @param weight 物品重量数组
* @param value 物品价值数组
* @param num 每个物品最多能放多少个数组
* @return
*/
public static int multiplePack(int V, int N, int[] weight, int[] value, int num[]){
int[][] dp = new int[N+1][V+1];
Map timesMap = Maps.newHashMap();
for(int i=1; i dp[i-1][j]){
timesMap.put(i, k);
}
}
}else{
dp[i][j] = dp[i-1][j];
}
}
}
//打印所有背包内的物品编号-方法一
int j = V;
for(int i=N; i >0; i--){
if(dp[i][j] > dp[i-1][j]){
int k = timesMap.get(i);
System.out.print(i + ": " + k + "次, ");
j = j - k * weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
//打印所有背包内的物品编号-方法二
j = V;
for(int i=N; i >0; i--){
while(dp[i][j] > dp[i-1][j]){
System.out.print(i + ", ");
j = j - weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
System.out.println("multiplePack maxValue: " + dp[N][V]);
return dp[N][V];
}
三、完全背包
1.二维数组方法
/**
* 完全背包---二维数组方法
* 1. 不装入第i种物品,即dp[i−1][j],同01背包;
* 2. 装入第i种物品,此时和01背包不太一样,因为每种物品有无限个(但注意书包限重是有限的),
* 所以此时不应该转移到dp[i−1][j−w[i]]而应该转移到dp[i][j−w[i]],即装入第i种商品后还可以再继续装入第种商品。
* 所以状态转移方程为:
* dp[i][j] = max(dp[i−1][j], dp[i][j−w[i]]+v[i]) // j >= w[i]
*
*
* @param V 背包总容量(重量)
* @param N 物品个数
* @param weight 物品重量数组
* @param value 物品价值数组
* @return
*/
public static int completePack(int V, int N, int[] weight, int[] value){
int[][] dp = new int[N+1][V+1];
//下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
Map timesMap = Maps.newHashMap();
for(int i=1; i0; i--){
while(dp[i][j] > dp[i-1][j]){
System.out.print(i + ", ");
j = j - weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
System.out.println("completePack maxValue: " + dp[N][V]);
return dp[N][V];
}
2.一维数组方法
/**
* 完全背包---一维数组方法
* 只用一个一维数组记录状态,dp[i]表示容量为i的背包所能装入物品的最大价值
* 用顺序来实现
*
* @param V 背包总容量(重量)
* @param N 物品个数
* @param weight 物品重量数组
* @param value 物品价值数组
* @return
*/
public static int completePack_02(int V, int N, int[] weight, int[] value){
int[] dp = new int[V+1];
//下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
Map timesMap = Maps.newHashMap();
for(int i=1; i0; i--){
while(dp[j] < dp[j-weight[i-1]] + value[i-1]){
System.out.print(i + ", ");
j = j - weight[i-1];
}
if(j == 0){
break;
}
}
System.out.println("");
System.out.println("completePack_02 maxValue: " + dp[V]);
return dp[V];
}
四、测试
public static void main(String[] args) {
int V = 10;
int N = 4;
int[] weight = {5, 4, 6, 3};
int[] value = {10, 40, 30, 50};
int[] num = {1, 2, 3, 4};
zeroOnePack(V, N, weight, value);
zeroOnePack_02(V, N, weight, value);
multiplePack(V, N, weight, value, num);
completePack(V, N, weight, value);
completePack_02(V, N, weight, value);
}