描述:有n个物品,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?
思路:
-
明确dp数组的意义:dp[i] [j]表示,当我现在只有前i个物品以及j个空间时,背包能带走的最大价值
-
寻找递推关系:当遍历到某个物品时,我们所面临的问题是,装不装这个物品
-
装:上一个物品计算之后加上这个物品的价值,dp[i - 1] [j – weight[i]] + value[i]
-
不装:上一个物品计算之后的价值,dp[i - 1] [j]
-
-
初始化dp数组:找到显而易见的初始值
当剩余空间为0,价值也为0:dp[i] [0] = 0
代码:
/**
* 01背包问题
* @param weight n个物品的重量
* @param value n个物品的价值
* @param total 背包总重量
* @return 能带走的最大价值
*/
public int zeroAndOneBag(int[] weight, int[] value, int total){
int n = weight.length;
// dp[i][j]:有前i个物品,j个容量时的最大价值
int[][] dp = new int[n + 1][total + 1];
// 初始化
for (int i = 0; i <= n; i++) dp[i][0] = 0;
// 递推
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= total; j++) {
// 空间是否能装下当前物品
if(j >= weight[i - 1]){
// 能装下,就看你选择装或者不装
dp[i][j] = Math.max(dp[i - 1][j - weight[i - 1]] + value[i - 1], dp[i - 1][j]);
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][total];
}
描述:有n种物品,每种物品有无限个,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?
和01背包中的问题只有一个区别,完全背包的物品可以放置多次,01问题中使用二维数组,就是为了控制不同的物品最多使用一次
代码
/**
* 完全背包问题
* @param weight n个物品的重量
* @param value n个物品的价值
* @param total 背包的所能承受的重量
* @return 最大价值
*/
public int completeBag(int[] weight, int[] value, int total){
// 物品的种数
int n = weight.length;
// dp[j] 表示有j个空间,所能带走的最大价值
int[] dp = new int[total + 1];
dp[0] = 0;
for (int i = 0; i < n; i++) {
for (int j = weight[i]; j <= total; j++) {
dp[j] = Math.max(dp[j - weight[i]] + value[i], dp[j]);
}
}
return dp[total];
}
描述:有n种物品,每种物品有amount[i]个,每个物品的重量为weight[i],每个物品的价值为value[i]。现在有一个背包,它所能容纳的重量为total,问:当你面对这么多有价值的物品时,你的背包所能带走的最大价值是多少?
和完全背包类似,区别在于每种物品的个数是有限制的。用一个count[]数组来存储当前容量下,取得最大价值已经要用到本类物品多少个,如果当前物品没用完,则产生比较;如果当前物品用完了,即使装上它价值大,也不装它。
注:count[i]放入第i个物品的操作是基于count[j-weight[i]]放入的
/**
* 多重背包
* @param amount 每种物品的数量
* @param weight 每种物品的重量
* @param value 每种物品的价值
* @param total 背包能承受的总重量
* @return 最大价值
*/
public int multiple(int[] amount, int[] weight, int[] value, int total){
int n = weight.length;
int[] dp = new int[total + 1];
dp[0] = 0;
for (int i = 0; i < n; i++) {
// count[j] 表示 当前容量已经用了当前种类物品的个数
int[] count = new int[total + 1];
for (int j = weight[i]; j <= total; j++) {
// 当前种类的物品还没有用完
if(count[j - weight[i]] < amount[i]){
int temp = dp[j - weight[i]] + value[i];
// 如果选了当前物品
if(temp >= dp[j]){
dp[j] = temp;
// 放入第i个物品的操作是基于count[j-weight[i]]放入的
count[j] = count[j - weight[i]] + 1;
}
continue;
}
if(dp[j] == 0){
dp[j] = dp[j - 1];
}
count[j] = count[j-1];
}
}
return dp[total];
}