完全背包-动态规划

230 阅读2分钟

完全背包问题是另一种背包问题,与0-1背包问题相似,但有一个重要区别:在完全背包问题中,每种物品可以选择多次放入背包,而不像0-1背包问题中一种物品只能选择一次。

解决完全背包问题通常仍然使用动态规划。以下是解决完全背包问题的基本步骤:

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

2.状态转移方程:状态转移方程也与0-1背包问题类似,但有一个重要区别,即在选择第i种物品时,可以多次选择,因此需要考虑多种选择情况。状态转移方程为:

3.dp[i][j] = max(dp[i-1][j], dp[i][j - weight[i]] + value[i]),其中weight[i] 是第i种物品的重量,value[i] 是第i种物品的价值。

4.初始化:初始化dp数组的第一行和第一列,通常为0,表示没有物品或背包容量为0时的最大总价值。

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

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

public class KnapsackProblemComplete {

    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;
        }

        public String toString() {
            return "Item(" +
                    "index=" + index +
                    ')';
        }
    }

    public static void main(String[] args) {
        Item[] items = {
                new Item(1, 2, 3, "青铜"),  //c
                new Item(2, 3, 4, "白银"),  //s
                new Item(3, 4, 7, "黄金"),  //a
        };
        System.out.println(select(items, 6));
        System.out.println();
    }
    
    /**
     * 0   1   2   3   4   5   6
     * 1   0   0   c   c   cc  cc  ccc
     * 2   0   0   c   s   cc  sc  ccc
     * 3   0   0   c   a   a   a   ac
     * 
     * if(放得下){
     * d[i][j] = max(dp[i-1][j],dp[i][j-item.weight]+item.value)
     * }else{
     * dp[i][j] = dp[i-1][j]
     * }
     */

    private 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] = dp[0][j - item0.weight] + item0.value;
            }
        }
        print(dp);

        for (int i = 1; i < items.length; i++) {
            for (int j = 0; j < total+1; j++) {
                Item item = items[i];
                int x = dp[i - 1][j]; //上次的最大价值
                if(j >= item.weight){ //放得下
                    //                          剩余空间能装的最大价值 + 当前物品价值
                    dp[i][j] = Integer.max(x,dp[i][j-item.weight]+item.value);
                }else {  //放不下
                    dp[i][j] = x;
                }
            }
            print(dp);
        }

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

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

}