0-1背包问题

5 阅读3分钟

0-1 背包问题:有限容量下的最大价值

1. 问题定义

0-1 背包问题是最经典的动态规划问题,描述如下:

给定 n 个物品,每个物品有重量 w[i]价值 v[i],背包最大容量为 C。每个物品只能选择 “放入” 或 “不放入” 背包(0-1 选择),求在背包容量限制下,能装入的最大价值

例如:物品重量 [1,2,3],价值 [6,10,12],背包容量 5,最大价值为 22(选重量 2 和 3 的物品,价值 10+12=22)。

2. 核心求解策略

0-1 背包是多项式时间可解的问题(P 类问题),核心解法是动态规划,且可通过空间优化降低复杂度:

基础 DP 思路(二维数组)

  • 状态定义dp[i][j] 表示 “前 i 个物品,背包容量为 j 时的最大价值”。

  • 状态转移

    • 不选第 i 个物品:dp[i][j] = dp[i-1][j]
    • 选第 i 个物品(需满足 j ≥ w[i]):dp[i][j] = max(dp[i][j], dp[i-1][j-w[i]] + v[i])

空间优化(一维数组)

二维 DP 的空间复杂度为 O(n*C),可优化为 O(C)

  • 状态定义:dp[j] 表示 “背包容量为 j 时的最大价值”;
  • 状态转移:逆序遍历容量 j(避免重复选择同一物品),dp[j] = max(dp[j], dp[j-w[i]] + v[i])

3. Java 代码实现(一维 DP 优化版)

import java.util.Arrays;

/**
 * 0-1背包问题 - 一维动态规划优化解法
 * 空间复杂度O(C),时间复杂度O(n*C)
 */
public class ZeroOneKnapsack {
    /**
     * 求解0-1背包最大价值
     * @param weights 物品重量数组
     * @param values 物品价值数组
     * @param capacity 背包最大容量
     * @return 最大价值
     */
    public static int knapsack(int[] weights, int[] values, int capacity) {
        int n = weights.length;
        // dp[j]:容量为j的背包能装的最大价值
        int[] dp = new int[capacity + 1];

        // 初始化:容量为0时,价值为0(数组默认值为0,无需额外初始化)
        Arrays.fill(dp, 0);

        // 遍历每个物品
        for (int i = 0; i < n; i++) {
            int w = weights[i];
            int v = values[i];
            // 逆序遍历容量:避免重复选择同一物品(0-1特性)
            for (int j = capacity; j >= w; j--) {
                // 状态转移:选或不选当前物品,取最大值
                dp[j] = Math.max(dp[j], dp[j - w] + v);
            }
        }

        return dp[capacity];
    }

    public static void main(String[] args) {
        // 测试用例:物品重量[1,2,3],价值[6,10,12],背包容量5
        int[] weights = {1, 2, 3};
        int[] values = {6, 10, 12};
        int capacity = 5;

        int maxValue = knapsack(weights, values, capacity);
        System.out.println("0-1背包最大价值:" + maxValue); // 预期结果:22
    }
}

4. 代码详解

  1. 数组初始化dp 数组长度为 capacity+1,初始值全为 0(容量为 0 时价值为 0)。

  2. 物品遍历:外层循环遍历每个物品,获取当前物品的重量 w 和价值 v

  3. 逆序容量遍历

    • 逆序遍历是 0-1 背包的核心(区别于完全背包的正序),确保每个物品仅被选择一次;
    • 仅遍历 j ≥ w 的容量(容量不足时无法选择当前物品);
    • 状态转移:比较 “不选当前物品(dp [j])” 和 “选当前物品(dp [j-w]+v)” 的价值,取最大值。
  4. 结果返回dp[capacity] 即为 “背包容量为 capacity 时的最大价值”。