解决01背包问题的动态规划算法
在Java系列的第一篇内容中,我将介绍如何使用Java编写一个动态规划算法来解决01背包问题。01背包问题是一个经典的组合优化问题,涉及在有限的背包容量下,选择哪些物品以最大化价值。我将逐步讲解算法的实现,希望这个示例可以帮助你理解动态规划和01背包问题的基本原理。
一、问题描述
假设有一组物品,每个物品都有一个重量和一个价值,以及一个背包,它有一个固定的容量。我们的目标是选择哪些物品放入背包,以便它们的总重量不超过背包的容量,并且它们的总价值最大化。但是,每个物品只能选择一次,要么放入背包,要么不放。
二、动态规划算法
2.1思想
动态规划的基本思想是将一个大问题分解成许多小问题,并在解决小问题的过程中保存已解决的子问题的结果,以避免重复计算,从而提高效率。
2.2核心步骤
- 定义问题的状态:确定需要优化的变量,并定义它们的状态。
- 定义状态转移方程:确定如何从一个状态转移到下一个状态,描述问题的最优子结构。
- 初始化状态:将初始状态设置为问题的初始条件。
- 计算和存储结果:使用状态转移方程计算并存储每个状态的结果。
- 返回最终解:根据已计算的结果找到最终解决方案。
三、代码思路
1. 定义问题
首先,明确定义01背包问题。描述问题的输入是什么,包括物品的重量和价值,背包的容量等。也要明确问题的目标,即在给定的限制条件下,如何选择物品以最大化总价值。
2. 确定问题规模
确定问题的规模,包括物品的数量(n),物品的重量数组(weights),物品的价值数组(values),以及背包的容量(capacity)。
3. 创建动态规划表
创建一个二维数组(dp),其中 dp[i][w]
表示在考虑前i个物品时,背包容量为w时的最大价值。初始化这个表格,通常将第0行和第0列设置为0,因为在没有物品或背包容量为0时,最大价值都是0。
4. 编写动态规划算法
使用嵌套的循环来填充动态规划表。外层循环遍历物品,内层循环遍历背包容量。在每个单元格 dp[i][w]
中,根据以下条件计算最大价值:
- 如果当前没有物品可以选择或背包容量为0时,最大价值为0
- 如果第i个物品的重量小于等于w,则可以考虑放入或不放入该物品,选择最大价值。
- 如果第i个物品的重量大于w,则无法放入该物品,最大价值保持不变。
这部分的核心代码如下:
if (i == 0 || w == 0){
dp[i][w] = 0;
}
else if (weights[i - 1] <= w) {
dp[i][w] = Math.max(values[i - 1] + dp[i - 1][w - weights[i - 1]], dp[i - 1][w]);
} else {
dp[i][w] = dp[i - 1][w];
}
5. 返回最优解
计算完整个动态规划表格后,最优解通常存储在 dp[n][capacity]
中,其中n是物品的数量,capacity是背包的容量。返回这个值作为问题的答案。
四、完整代码展示
/*
* @author chan
* @data 2023-09-02
* @question 01背包问题
* @method 动态规划
*/
public class Knapsack_problem_01 {
public static void main(String[] args) {
//定义物品的重量和价值
int[] weight = {2,3,4,5};
int[] values = {3,4,5,6};
//定义背包的容量
int capacity = 5;
//调用方法
int maxValues = Knapsack(weight,values,capacity);
System.out.println("maxValues is :" + maxValues);
}
//定义方法
public static int Knapsack(int[] weight,int[] values,int capacity){
//创建的动态规划表的大小是根据问题的规模而确定的,其中行数和列数分别对应物品的数量和背包的容量。
int n = weight.length;
//由于索引二维数组下标均为从0开始故均+1
int[][] dp = new int[n + 1][capacity + 1];
//进入外层循环
for (int i = 0;i <= n;i++){
//内层循环
for (int w = 0;w <=capacity;w++){
//如果当没有物品可选或背包容量为0时,最大价值为0
if (i == 0 || w == 0){
// 如果当前物品的重量小于等于背包容量
// 可以选择放入或不放入,然后Math函数判断选择最大价值
dp[i][w] = 0;
} else if (weight[i - 1] <= w) {
//放入第i个物品容量为w时 其最大价值等于第i个物品的价值加上剩余容量w减去第i个物品重量后,前i-1个物品在这个容量下的最大价值
//不放入第i个物品容量为w时 其最大价值保持为 dp[i - 1][w],即前i-1个物品在容量w下的最大价值
dp[i][w] = Math.max(values[i - 1] + dp[i - 1][w - weight[i - 1]],dp[i-1][w]);
}else{
//如果当前物品的重量大于背包容量,无法放入
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
}