问题描述
一个旅行者外出旅行时需要将 n 件物品装入背包,背包的总容量为 m。每个物品都有一个重量和一个价值。你需要根据这些物品的重量和价值,决定如何选择物品放入背包,使得在不超过总容量的情况下,背包中物品的总价值最大。
给定两个整数数组 weights 和 values,其中 weights[i] 表示第 i 个物品的重量,values[i] 表示第 i 个物品的价值。你需要输出在满足背包总容量为 m 的情况下,背包中物品的最大总价值。
动态规划思路
1. 状态定义
我们定义一个一维数组 dp,其中 dp[j] 表示当背包容量为 时的最大总价值。
2. 初始化状态
- 初始状态
dp[0]应该是 0,表示当背包容量为 0 时,价值自然为 0。 - 当背包容量大于 0 时,尚未放入任何物品,初始值可以任意(通常默认为 0)。
3. 选择物品
对于每一个物品 ,可以选择把它放入背包或者不放入背包。如果放入背包,则当前背包容量至少应大于等于该物品的重量。
4. 状态转移方程
我们从后向前更新 DP 数组的情况,防止在同一轮更新中多次选择同一物品。对于每个物品 ,我们有以下转移方程:
dp[j]=max(dp[j],dp[j-weights[i]+values[i])
dp[j]表示当前背包容量为 时的最大价值。dp[j - weights[i]] + values[i]表示选择物品 后的最大价值。
复杂度分析
-
时间复杂度:
- 总共遍历每一个物品n次,每次遍历背包容量从m到物品重量。因此时间复杂度为O(nm) 。
-
空间复杂度:
- 只使用了一个一维数组
dp,其长度为m+1,因此空间复杂度为O(m) 。
- 只使用了一个一维数组
示例解析
示例输入:
n = 3
m = 3
weights = [2, 1, 3]
values = [4, 2, 3]
运行步骤:
-
初始化:
dp = [0, 0, 0, 0] -
遍历物品,更新 DP 数组:
-
物品 1 (weights[0] = 2, values[0] = 4):
- 更新
dp[2]= max(0, 4) →[0, 0, 4, 0] - 更新
dp[3]= max(0, 4) →[0, 0, 4, 4]
- 更新
-
物品 2 (weights[1] = 1, values[1] = 2):
- 更新
dp[1]= max(0, 2) →[0, 2, 4, 4] - 更新
dp[2]= max(4, 2 + 0) →[0, 2, 4, 4] - 更新
dp[3]= max(4, 2 + 2) →[0, 2, 4, 6]
- 更新
-
物品 3 (weights[2] = 3, values[2] = 3):
- 更新
dp[3]= max(6, 0 + 3) →[0, 2, 4, 6]
- 更新
-
最终,dp[3] = 6,表示在背包容量为 3 的情况下,可以获得的最大价值为 6(选择物品 1 和物品 2)。
最终代码
public class Main {
public static int solution(int n, int[] weights, int[] values, int m) {
// write code here
int[] dp=new int[m+1]; //dp[i]表示i容量最多能装的价值
for(int i=0;i<n;i++){
for(int j=m;j>=weights[i];j--){
dp[j]=Math.max(dp[j],dp[j-weights[i]]+values[i]);
}
}
return dp[m];
}
public static void main(String[] args) {
System.out.println(solution(3, new int[]{2, 1, 3}, new int[]{4, 2, 3}, 3) == 6);
System.out.println(solution(4, new int[]{1, 2, 3, 2}, new int[]{10, 20, 30, 40}, 5) == 70);
System.out.println(solution(2, new int[]{1, 4}, new int[]{5, 10}, 4) == 10);
}
}