背包问题--动态规划

问题描述:

一个背包的总容量为V,现在有N类物品,第i类物品的重量为weight[i],价值为value[i]
那么往该背包里装东西,怎样装才能使得最终包内物品的总价值最大。这里装物品主要由三种装法:
1、0-1背包:每类物品最多只能装一次
2、多重背包:每类物品都有个数限制,第i类物品最多可以装num[i]次
3、完全背包:每类物品可以无限次装进包内

一、01背包问题

1.二维数组方法

 /**
     * 0-1背包--二维数组方法
     * 状态转移方程为
     *
     * dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >= w[i]
     *
     *
     * @param V 背包总容量(重量)
     * @param N 物品个数
     * @param weight 物品重量数组
     * @param value 物品价值数组
     * @return
     */
    public static int zeroOnePack(int V, int N, int[] weight, int[] value){

        int[][] dp = new int[N+1][V+1];
        //下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
        for(int i=1; i0; i--){
            if(dp[i][j] > dp[i-1][j]){
                System.out.print(i + ", ");
                j = j-weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        System.out.println("");
        System.out.println("zeroOnePack maxValue: " + dp[N][V]);
        return dp[N][V];
    }

2.一维数组方法

/**
     * 0-1背包--一维数组方法
     * 当背包容量为j时,已经放入了i件物品,最大价值为d[j]
     * 当第i件物品比背包容量还大时,d[j]=d[j];
     * 当第i件物品比背包容量小时,就要判断两种情况:不放的话,和上式相同,放的话,背包容量-第i件物品的质量再去装前i-1件物品,
     * 所得得最大价值加上第i件物品的价值,两者较大值即为最优解。d[j]=max(dp[j],dp[j-w[i]]+v[i])
     *
     * 状态转移方程为:
     * dp[j] = Math.max(dp[j-weight[i-1]]+value[i-1],dp[j]);
     *
     *
     * @param V 背包总容量(重量)
     * @param N 物品个数
     * @param weight 物品重量数组
     * @param value 物品价值数组
     * @return
     */
    public static int zeroOnePack_02(int V, int N, int[] weight, int[] value){

        int[] dp = new int[V+1];
        for(int i=1; i=weight[i-1]; j--){
                //由于weight和value数组下标都是从0开始,注意第i个物品的重量为weight[i-1],价值为value[i-1]
                 dp[j] = Math.max(dp[j], dp[j-weight[i-1]] + value[i-1]);
                System.out.print(dp[j] + ", ");
            }
            System.out.println("--------");
        }

        //打印所有背包内的物品编号
        int j = V;
        for(int i=N; i >0; i--){
            if(dp[j] < dp[j-weight[i-1]] + value[i-1]){
                System.out.print(i + ", ");
                j = j-weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        System.out.println("");
        System.out.println("zeroOnePack_02 maxValue: " + dp[V]);
        return dp[V];
    }

二、多重背包

1.二维数组方法

/**
     * 多重背包
     * k为装入第i种物品的件数, k <= min(n[i], j/w[i])
     * dp[i][j] = max{(dp[i-1][j − k*w[i]] + k*v[i]) for every k}
     *
     *
     * @param V 背包总容量(重量)
     * @param N 物品个数
     * @param weight 物品重量数组
     * @param value 物品价值数组
     * @param num 每个物品最多能放多少个数组
     * @return
     */
    public static int multiplePack(int V, int N, int[] weight, int[] value, int num[]){
        int[][] dp = new int[N+1][V+1];
        Map timesMap = Maps.newHashMap();
        for(int i=1; i dp[i-1][j]){
                            timesMap.put(i, k);
                        }
                    }
                }else{
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        //打印所有背包内的物品编号-方法一
        int j = V;
        for(int i=N; i >0; i--){
            if(dp[i][j] > dp[i-1][j]){
                int k = timesMap.get(i);
                System.out.print(i + ": " + k + "次, ");
                j = j - k * weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        System.out.println("");
        //打印所有背包内的物品编号-方法二
        j = V;
        for(int i=N; i >0; i--){
            while(dp[i][j] > dp[i-1][j]){
                System.out.print(i  + ", ");
                j = j - weight[i-1];
            }
            if(j == 0){
                break;
            }
        }

        System.out.println("");
        System.out.println("multiplePack maxValue: " + dp[N][V]);
        return dp[N][V];
    }

三、完全背包

1.二维数组方法

/**
     * 完全背包---二维数组方法
     * 1. 不装入第i种物品,即dp[i−1][j],同01背包;
     * 2. 装入第i种物品,此时和01背包不太一样,因为每种物品有无限个(但注意书包限重是有限的),
     * 所以此时不应该转移到dp[i−1][j−w[i]]而应该转移到dp[i][j−w[i]],即装入第i种商品后还可以再继续装入第种商品。
     * 所以状态转移方程为:
     * dp[i][j] = max(dp[i−1][j], dp[i][j−w[i]]+v[i]) // j >= w[i]
     *
     *
     * @param V 背包总容量(重量)
     * @param N 物品个数
     * @param weight 物品重量数组
     * @param value 物品价值数组
     * @return
     */
    public static int completePack(int V, int N, int[] weight, int[] value){

        int[][] dp = new int[N+1][V+1];
        //下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
        Map timesMap = Maps.newHashMap();
        for(int i=1; i0; i--){
            while(dp[i][j] > dp[i-1][j]){
                System.out.print(i  + ", ");
                j = j - weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        System.out.println("");
        System.out.println("completePack maxValue: " + dp[N][V]);
        return dp[N][V];
    }

2.一维数组方法

 /**
     * 完全背包---一维数组方法
     * 只用一个一维数组记录状态,dp[i]表示容量为i的背包所能装入物品的最大价值
     * 用顺序来实现
     *
     * @param V 背包总容量(重量)
     * @param N 物品个数
     * @param weight 物品重量数组
     * @param value 物品价值数组
     * @return
     */
    public static int completePack_02(int V, int N, int[] weight, int[] value){

        int[] dp = new int[V+1];
        //下标从1开始,将dp[i][0]和dp[0][j]默认均置为0
        Map timesMap = Maps.newHashMap();
        for(int i=1; i0; i--){
            while(dp[j] < dp[j-weight[i-1]] + value[i-1]){
                System.out.print(i  + ", ");
                j = j - weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        System.out.println("");
        System.out.println("completePack_02 maxValue: " + dp[V]);
        return dp[V];
    }

四、测试

 public static void main(String[] args) {
        int V = 10;
        int N = 4;
        int[] weight = {5, 4, 6, 3};
        int[] value = {10, 40, 30, 50};
        int[] num = {1, 2, 3, 4};
        zeroOnePack(V, N, weight, value);
        zeroOnePack_02(V, N, weight, value);
        multiplePack(V, N, weight, value, num);
        completePack(V, N, weight, value);
        completePack_02(V, N, weight, value);


    }
image.png

你可能感兴趣的:(背包问题--动态规划)