0-1背包-动态规划

156 阅读3分钟

0-1背包问题是一个经典的组合优化问题,通常使用动态规划算法来解决。在这个问题中,给定一组物品,每个物品都有一个重量和一个价值,以及一个固定容量的背包。目标是选择一些物品放入背包,使得它们的总重量不超过背包的容量,同时总价值最大。

下面是使用动态规划解决0-1背包问题的基本步骤:

  1. 定义状态:创建一个二维数组dp,其中dp[i][j]表示在前i个物品中,背包容量为j时的最大总价值。这个数组的行表示物品的数量,列表示背包容量。

  2. 状态转移方程:定义状态转移方程,根据以下情况进行更新:

    • 如果第i个物品的重量大于当前背包容量j,那么无法将该物品放入背包,所以dp[i][j] = dp[i-1][j]

    • 否则,考虑两种情况,选择第i个物品或者不选择第i个物品,取两者中的最大值:

      • dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i]),其中weight[i]是第i个物品的重量,value[i]是第i个物品的价值。
  3. 初始化:初始化dp数组的第一行和第一列,通常为0,表示没有物品或背包容量为0时的最大总价值。

  4. 填充DP表格:按照状态转移方程,从左上角向右下角逐步填充dp数组。

  5. 最终结果dp[n][capacity]就是在n个物品中,背包容量为capacity时的最大总价值,其中n是物品的数量。

  6. 还原最优解(可选):如果需要找出具体的选择方案,可以倒推回去,从dp[n][capacity]开始,根据状态转移方程的选择来确定哪些物品被放入背包。

  /**
     * 0-1 背包问题
     * 1.n个物品都是固体,有重量和价值
     * 2.现在你要取走不超过 10克 的物品
     * 3.每次可以不拿或全拿,问最高价值是多少
     * 编号    重量(g)      价值(元)
     * 1        4          1600       黄金一块          A
     * 2        8          2400       红宝石一枚        R
     * 3        5          30         白银一块          S
     * 0        1          1_000_000  钻戒一枚          D
     *
     *      0   1   2   3   4   5   6   7   8   9   10
     * 0    0   0   0   0   A   A   A   A   A   A   A   黄金
     * 1    0   0   0   0   A   A   A   A   R   R   R   红宝石
     * 2    0   0   0   0   A   A   A   A   R   R   R   白银
     * 3    0   D   D   D   D   DA  DA  DA  DA  DR  DR  钻石
     */
public class KnapsackProblem {
    static class Item {

        //索引
        int index;
        //重量
        int weight;
        //价值
        int value;
        //名字
        String name;

        public Item(int index, int weight, int value, String name) {
            this.index = index;
            this.weight = weight;
            this.value = value;
            this.name = name;
        }
    }

    static int select(Item[] items, int total) {
        int[][] dp = new int[items.length][total + 1];
        Item item0 = items[0];
        for (int j = 0; j < total + 1; j++) {
            if (j >= item0.weight) {  //装的下
                dp[0][j] = item0.value;
            }else {  //装不下
                dp[0][j] = 0;
            }
        }
        print(dp);

        for (int i = 1; i < dp.length; i++) {
            Item item = items[i];
            for (int j = 0; j < total + 1; j++) {
                if(j >= item.weight){ //装得下
                    dp[i][j] = Integer.max(dp[i-1][j],item.value + dp[i-1][j-item.weight]);
                }else { //装不下
                    dp[i][j] = dp[i-1][j];
                }
            }
            print(dp);
        }

        return dp[dp.length-1][total];
    }

    static void print(int[][] dp){
        for (int i = 0; i < dp.length; i++) {
            System.out.println(Arrays.toString(dp[i]));
        }
    }

    public static void main(String[] args) {

        Item[] items = new Item[]{
                new Item(1, 4, 1600, "黄金"),
                new Item(2, 8, 2400, "红宝石"),
                new Item(3, 5, 30, "白银"),
                new Item(4, 1, 1000000, "钻石"),
        };
        System.out.println(select(items, 10));

    }

}