public static int solution(int n, int[] weights, int[] values, int m) {
// 定义 dp 数组
int[][] dp = new int[n + 1][m + 1];
// 遍历每个物品
for (int i = 1; i <= n; i++) {
int weight = weights[i - 1];
int value = values[i - 1];
// 遍历每个容量
for (int j = 0; j <= m; j++) {
// 如果不选当前物品
dp[i][j] = dp[i - 1][j];
// 如果选择当前物品
if (j >= weight) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value);
}
}
}
return dp[n][m];
}
这段代码实现了一个经典的动态规划算法,用于解决0-1背包问题。下面是对代码的逐行解释:
背包问题概述
在传统的0-1背包问题中,有一组物品,每种物品都有一个重量和一个价值。在当前的背包容量允许的情况下,目标是选择物品以便最大化放入背包的总价值。每种物品只能选择一次(即要么选择放入,要么不放)。
代码解析
public static int solution(int n, int[] weights, int[] values, int m) {
n: 物品的数量。weights: 每个物品的重量数组。values: 每个物品的价值数组。m: 背包的最大容量。
// 定义 dp 数组
int[][] dp = new int[n + 1][m + 1];
- 初始化一个二维数组
dp。dp[i][j]表示前i个物品放入容量为j的背包时的最大总价值。
主循环:遍历每个物品
for (int i = 1; i <= n; i++) {
int weight = weights[i - 1];
int value = values[i - 1];
- 使用
i遍历每个物品。weights[i - 1]和values[i - 1]用来获取当前物品的重量和价值(由于数组索引从0开始,所以下标减1)。
内层循环:遍历每个容量
for (int j = 0; j <= m; j++) {
- 使用
j遍历从0到m的每个可能的背包容量。
// 如果不选当前物品
dp[i][j] = dp[i - 1][j];
- 如果不选择当前物品
i,则最大价值就是前i-1个物品在容量j下的最大价值。
// 如果选择当前物品
if (j >= weight) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value);
}
- 如果选择当前物品并且当前容量
j大于等于该物品的重量,程序会考虑两种选择:- 不选择物品
i,保持dp[i][j]的值。 - 选择物品
i,则可以得到的价值为dp[i - 1][j - weight] + value,这即为放弃当前物品j - weight的最大价值加上当前物品的价值。
- 不选择物品
- 使用
Math.max选择这两种方案中的最大值。
返回结果
return dp[n][m];
}
- 最终返回
dp[n][m],即所有n个物品放入容量为m的背包时的最大价值。
总结
这个算法的时间复杂度为 O(n * m),因为有两个嵌套的循环分别遍历物品数和背包容量。空间复杂度也是 O(n * m),用于存储 dp 数组。如果需要进一步优化空间,也可以使用一维数组,但这就需要额外的技巧以保持正确性。在实际使用中,这种动态规划方法非常有效,尤其是在物品数量与容量的范围较小时。
这段代码实现了著名的0-1背包问题的动态规划解法。下面详细解释背包问题的原理、代码的逻辑以及如何使用动态规划解决这个问题。
0-1背包问题简介
0-1背包问题是一个优化问题,给定一组物品,每个物品有一定的重量和价值,以及一个背包的最大承重,目标是选择物品,使得它们的总价值最大化,并且总重量不超过背包的承重限制。物品数量为有限,且每个物品只能选择一次。
代码逻辑
-
定义动态规划数组:
int[][] dp = new int[n + 1][m + 1];dp[i][j]表示前i个物品在承重为j的情况下能够达到的最大价值。- 数组的大小是
(n + 1) x (m + 1),其中n是物品数量,m是背包的最大承重。+1是为了考虑0个物品和0承重的情况。
-
遍历每个物品:
for (int i = 1; i <= n; i++) { int weight = weights[i - 1]; int value = values[i - 1]; ... }- 从 1 到
n遍历每个物品。由于dp数组的维度是n + 1,这里从1开始方便处理0个物品的情况。
- 从 1 到
-
遍历每个容量:
for (int j = 0; j <= m; j++) {- 对于每个可用的承重
j,首先假设不选择当前物品,即dp[i][j] = dp[i - 1][j]。
- 对于每个可用的承重
-
选择当前物品:
if (j >= weight) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value); }- 如果当前物品可以加入背包(即当前承重
j大于等于物品的重量),则计算选择该物品后的总价值:dp[i - 1][j - weight] + value表示选择该物品后,剩余承重j - weight中能获得的最大价值加上当前物品的价值。
- 更新
dp[i][j]为不选和选择当前物品的最大价值。
- 如果当前物品可以加入背包(即当前承重
-
返回结果:
return dp[n][m];- 最终
dp[n][m]代表使用前n个物品在承重最大为m的情况下能够获得的最大价值。
- 最终
复杂度分析
- 时间复杂度:O(n * m),其中
n是物品的数量,m是背包的最大承重。需要遍历所有物品和所有承重组合。 - 空间复杂度:O(n * m),用于存储动态规划表。如果需要节省空间,可以考虑用滚动数组优化。
总结
通过动态规划的方法,我们能够有效地解决0-1背包问题。代码利用二维数组 dp 来保存各个状态的信息,从而确保在后续阶段能利用之前的结果,并最终给出最大的包裹价值。