算法 - 0-1背包问题

有n件物品和一个最大承重为W的背包,每件物品的重量是w、价值是v
在保证总重量不超过W的前提下,选择某些物品装入背包,背包的最大总价值是多少?
注意:每个物品只有1件,也就是每个物品只能选择0件或者1件

  • 假设values是价值数组,weights是重量数组
    编号为k的物品,价值是values[k],重量是weights[k],k ∈ [0, n)
  • 假设dp(i, j) 是最大称重为j、前有i件物品可选时的最大中价值,i ∈ [1, n],j ∈ [1, W]
  1. dp(i, 0)、dp(0, j) 初始值均为0
  2. 如果 j < weights[i - 1],那么dp(i, j) = dp(i - 1, j)
  3. 如果 j > = weights[i - 1],那么dp(i, j) = max { dp(i - 1, j), dp(i - 1, j - weights[i - 1]) + valuse[i - 1] }

实现

int select(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (weights.length != values.length) return 0;
    if (capacity <= 0) return 0;
    int[][] dp = new int[values.length + 1][capacity.length + 1];
    for (int i = 1; i <= values.length; i++) {
        for (int j = 1; j <= capacity; j++) {
            if (j < weights[i - 1]) {
                dp[i][j] = dp[i - 1][j];
            } else {
                dp[i][j] = Math.max(dp[i - 1][j],
                            dp[i - 1][j - weights[i - 1]] + values[i - 1]);
            }
        }
    }
    return dp[values.length][capacity];
}
  • dp数组的计算结果如下所示
    在这里插入图片描述

实现 - 一维数组

  • dp(i, j)都是由dp(i - 1, k)推导出来的,也就是说,第i行的数据是由它的上一行第i - 1行推导出来的
    因此,可以使用一维数组来优化
    另外,由于k <= j,所以j的遍历应该由大到小,否则导致数据错乱
int select(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (weights.length != values.length) return 0;
    if (capacity <= 0) return 0;
    int[] dp = new int[capacity.length + 1];
    for (int i = 1; i <= values.length; i++) {
        for (int j = capacity; j >= 1; j--) {
            if (j < weights[i - 1]) continue;
           dp[j] = Math.max(dp[j],
                            dp[j - weights[i - 1]] + values[i - 1]);
        }
    }
    return dp[capacity];
}

实现 - 一维数组优化

  • 观察二维数组表,得出结论:j的下界可以从1改为weights[i - 1]
int select(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (weights.length != values.length) return 0;
    if (capacity <= 0) return 0;
    int[] dp = new int[capacity.length + 1];
    for (int i = 1; i <= values.length; i++) {
        for (int j = capacity; j >= weights[i - 1]; j--) {
           dp[j] = Math.max(dp[j],
                            dp[j - weights[i - 1]] + values[i - 1]);
        }
    }
    return dp[capacity];
}

0-1背包 - 恰好装满

有n件物品和一个最大承重为W的背包,每件物品的重量是w、价值是v
在保证总重量恰好等于W的前提下,选择某些物品装入背包,背包的最大总价值是多少?
注意:每个物品只有1件,也就是每个物品只能选择0件或者1件

  • dp(i, j)初始状态调整
    dp(i, 0) = 0,总重量恰好为0,最大总价值必然也是0
    dp(0, j) = -∞(负无穷),j >= 1,负数在这里代表无法恰好装满
    在这里插入图片描述

实现

int select(int[] values, int[] weights, int capacity) {
    if (values == null || values.length == 0) return 0;
    if (weights == null || weights.length == 0) return 0;
    if (weights.length != values.length) return 0;
    if (capacity <= 0) return 0;
    int[] dp = new int[capacity.length + 1];
    for (int j = 1; j <= capacity; j++) {
        dp[j] = Integer.MIN_VALUE;
    }
    for (int i = 1; i <= values.length; i++) {
        for (int j = capacity; j >= weights[i - 1]; j--) {
           dp[j] = Math.max(dp[j],
                            dp[j - weights[i - 1]] + values[i - 1]);
        }
    }
    return dp[capacity] < 0 ? -1 : dp[capacity];
}
发布了188 篇原创文章 · 获赞 19 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/songzhuo1991/article/details/104126944