01背包问题

350 阅读2分钟

背包问题

背包问题是一种组合优化的NP问题。可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中,背包的空间有限,但我们需要最大化背包内所装物品的价值。 背包问题通常出现在资源分配中,决策者必须分别从一组不可分割的项目或任务中进行选择,而这些项目又有时间或预算的限制。

01背包问题

题意概要: 有 n 个物品和一个容量为 W 的背包,每个物品有重量 w_i 和价值v_i 两种属性,要求选若干物品放入背包使背包中物品的总价值最大,且背包中物品的总重量不超过背包的容量。

由于每个物体只有两种可能的状态(放与不放),对应二进制中的 0 和 1,这类问题便被称为「0-1 背包问题」。

暴力法解决

时间复杂度为O(2^n),n表示物品数量,不行。

动态规划

定义状态:定义子问题

dp[i][j]:表示将 i 件物品装进限重为 j 的背包可以获得的最大价值;

状态转移方程:描述子问题之间的联系,需分类讨论:

基于子问题的最优解(前i-1个物品),对于第 i 个物品,有两种可能的状态:

  • 不放进背包:背包容量不变,价值也不变化,及继承之前子问题的结果,dp[i][j] = dp[i-1][j];
  • 放进背包:背包容量会减少 w[i],价值发生增加 v[i],dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);

放第 i 个物品,此时背包容量要减去w[i],dp[i - 1][j - w[i]]为剩余容量 j - w[i] 限制下的最大值(前i-1个物品最大值),然后加上第 i 个物品的价值 v[i]

状态转移方程:

image-20230330150949659.png

代码

public static int solution(int[] w, int[] v, int m) {
    int n = w.length; // 物品个数
    int[][] dp = new int[n][m + 1];
    for (int i = 0; i <= m; i++) {
        if (i >= w[0]) { // 初始化dp
            dp[0][i] = v[0];
        }
    }
    for (int i = 1; i < n; i++) { // 遍历物品
        for (int j = 1; j <= m; j++) { // 遍历重量
            if (w[i] > j) { // 不放物品(放不下)
                dp[i][j] = dp[i - 1][j];
            } else { // 放物品
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
            }
        }
    }
    // 最大价值
    return dp[n - 1][m];
}