01背包问题(二维数组解决)

216 阅读2分钟

问题描述:

N件物品和⼀个最多能背重量为W 的背包第i件物品的重量是weight[i],得到的价值是 value[i] 。每件物品只能⽤⼀次求解将哪些物品装⼊背包⾥物品价值总和最⼤?

例如:

背包最⼤重量为4,物品为: image.png 问背包能背的物品最⼤价值是多少?

思考

  1. dp[i][j]:表示从下标[0-i]的物品中任意取,放进容量为j的背包中得到的最大价值总和是多少。

image.png 2. dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),dp[i][j]有两种情况推出,dp[i][j]取这两种情况中的最大值,分别是:

  • 不放物品i:dp[i][j]=dp[i-1][j]
  • 放物品i:dp[i][j]=dp[i-1][j-weight[i]+value[i]];
  1. 初始化:首先如果背包容量j为0的话,即dp[i][0]=0,⽆论是选取哪些物品,背包价值总和⼀定为0;其次根据递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);i 是由 i-1 推导出来,那么i为0的时候就⼀定要初始化,即初始化dp[0][j]。当j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量⽐编号0的物品重量还⼩。当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放⾜够放编号0物品。

image.png 4. 确定遍历顺序:有两个遍历的维度:物品与背包重量。

  • 先遍历物品,再遍历背包 image.png
  • 先遍历背包,再遍历物品 image.png 可以看出不管是先遍历物品还是先遍历背包,根本不会影响递推公式的推导。所以两种遍历方式都可以。
  1. 推导dp数组 image.png

代码

var weight=[1,3,4],value=[15,20,30],n=3,m=4;
    test_2_wei_bag_problem1(n,m);

    function test_2_wei_bag_problem1(n,m){
        var dp=new Array(n).fill(0).map(_ =>new Array(m+1).fill(0));
        //初始化
        for(let i=0;i<n;i++){
            for(let j=0;j<=m;j++){
                if(weight[0]<=j){
                    dp[0][j]=value[0];
                }
                
            }
        }

        //两种遍历任选其一即可

        //遍历-先遍历背包再遍历物品
        for(let i=1;i<n;i++){
            for(let j=0;j<=m;j++){
                if(weight[i]<j){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
                }else{
                    dp[i][j]=dp[i-1][j];
                }
               
            }
        }

        //遍历-先遍历物品再遍历背包
        for(let j=0;j<=m;j++){
            for(let i=1;i<n;i++){
                if(weight[i]<j){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
                }else{
                    dp[i][j]=dp[i-1][j];
                }
               
            }
        }

        console.log(dp);
        return dp[n-1][m];
    }

结果:

image.png