01背包问题

154 阅读2分钟

题目描述

考题式描述

有N件物品和一个最多能背重量为W的背包(也就是说背包的容量是W),第i件物品的重量是weight[i],其价值是value[i],每件物品只能背一次,求解将哪些物品放到背包里面物品价值的总和最大。

简易版描述

贼,夜入豪宅,可偷之物甚多,而负重能力有限,偷哪些才更加不枉此行?

题目举例

物品编号1234
重量2132
价值3242
背包容量为5

解题思路

动态规划

根据举例中,直接找出答案比较困难,通过将其拆分成子问题,先从1个物品,背包容量为1时开始递推,最终找到答案。

确定动态规划公式:

编号前i个物品,背包容量为j时,所能取最大价值为 dp[i][j]

那么开始确定递推关系:

当前背包容量为j,当前物品的编号为i,我们要不要将物品i放入包内

  1. 当前物品编号i不放入背包中,dp[i][j] = dp[i-1][j]
    • 解释:不放入就是之前的值
  2. 当前物品编号i放入背包中,dp[i][j] = dp[i-1][j-weight[i]]+value[i]
    • 剩余空间能放的最大价值 加上 放入就是当前的价值
    • 如编号为2时,编号2的重量为1,放入后空间还剩5-1=4的空间,自然要根据前面填表中编号为1容量为4的最大值是多少算进去,方可总的最大

综上:在以上两种情况中选择最优的

dp[i][j] = Math.max( dp[i-1][j] , dp[i-1][j-weight[i]]+value[i] )

对样例进行填表

物品编号\容量012345
0000000
1003333
2023555
3023567
4023567

编程代码 以Java为例

import java.util.Arrays;

public class 背包问题 {
    public static void main(String[] args) {
        /**
         * 价值:v = {3, 2, 4, 2}
         * 重量:w = {2, 1, 3, 2}
         * 背包容量:C = 5
         */
        int[] w = {2,1,3,2};

        int[] v = {3,2,4,2};

        int C = 5;

        int len = w.length;

        int[][] bags = new int[5][C+1];

        for (int i = 1; i <= len; i++) {
            for (int j = 0; j <= C; j++) {
                //重量大于背包容量则不放入
                if (w[i - 1] > j) {
                    bags[i][j] = bags[i-1][j];
                }else {
                    bags[i][j] = Math.max(bags[i-1][j],bags[i-1][j-w[i-1]]+v[i-1]);
                }
            }
        }

        nums(bags);
    }

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