深入浅出 01 背包问题:原理与代码实现

01 背包是动态规划的经典入门题,“01” 的含义很简单 ——每个物品要么选(1),要么不选(0),没有中间的多次选择情况。今天用最通俗的方式,带你搞懂它的解题思路和代码实现。

一、问题描述

给定 n 个物品,每个物品对应两个属性:重量 w[i] 和 价值 v[i];还有一个容量为 C 的背包。目标:选若干物品装入背包,要求总重量不超过容量 C,且总价值最大。

举个例子:

  • 物品数量 n=3
  • 重量 w = [2, 3, 4],价值 v = [3, 4, 5]
  • 背包容量 C=5最优选择是拿前两个物品,总重量 2+3=5,总价值 3+4=7

二、动态规划核心思路

动态规划的关键是 状态定义 和 状态转移方程,01 背包的核心逻辑就藏在这里面。

1. 状态定义

定义二维数组 dp[i][j]前 i 个物品里选,装进容量为 j 的背包,能得到的最大价值

2. 状态转移方程

对第 i 个物品,只有两种选择:选 或 不选。我们要选价值更大的那个方案。

  1. 不选第 i 个物品:此时最大价值等于 dp[i-1][j](前 i-1 个物品在容量 j 下的最优解)。
  2. 选第 i 个物品:前提是背包容量够装它(j >= w[i]),此时价值等于 dp[i-1][j - w[i]] + v[i](前 i-1 个物品在剩余容量下的最优解,加上当前物品的价值)。

综上,状态转移方程为:

plaintext

dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i])  (j >= w[i]时)
dp[i][j] = dp[i-1][j]  (j < w[i]时,只能不选)

3. 初始化

  • 当 i=0(没有物品可选):不管背包容量多大,dp[0][j] = 0
  • 当 j=0(背包容量为 0):不管有多少物品,dp[i][0] = 0

三、代码实现(二维数组版)

以 Java 为例,逻辑清晰易懂:

java

运行

public class ZeroOneKnapsack {
    public static int maxValue(int[] w, int[] v, int C) {
        int n = w.length;
        // 二维dp数组,n+1行(前0~n个物品),C+1列(容量0~C)
        int[][] dp = new int[n + 1][C + 1];
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= C; j++) {
                // 注意:数组下标从0开始,i对应第i-1个物品
                if (j >= w[i-1]) {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - w[i-1]] + v[i-1]);
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n][C];
    }

    public static void main(String[] args) {
        int[] w = {2, 3, 4};
        int[] v = {3, 4, 5};
        int C = 5;
        System.out.println(maxValue(w, v, C)); // 输出7
    }
}

四、空间优化(一维数组版)

二维数组需要 O(n*C) 的空间,我们可以优化成 一维数组,空间复杂度降到 O(C)

核心思路:用一维数组 dp[j] 表示容量 j 的背包能装的最大价值。注意遍历顺序:容量 j 必须从后往前遍历,避免同一个物品被重复选择。

优化后代码(Java):

java

运行

public static int maxValueOpt(int[] w, int[] v, int C) {
    int n = w.length;
    int[] dp = new int[C + 1];
    
    for (int i = 0; i < n; i++) {
        // 从后往前遍历,防止重复选同一物品
        for (int j = C; j >= w[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
        }
    }
    return dp[C];
}

五、总结

01 背包的核心是 **“选或不选” 的状态转移 **,记住两个关键点:

  1. 二维数组的状态定义和转移方程,是理解的基础;
  2. 一维数组优化的关键是倒序遍历容量,避免物品重复选取。

掌握 01 背包后,你就能轻松应对它的各种变种问题(如恰好装满背包、求方案数等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值