01背包问题

解决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];
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值