01背包问题(动态规划),看完不懂我坐火车去教(zou)你

159 阅读3分钟

我一直都没搞懂01背包问题,直到我开始了面试.....

废话说完,进入正题

题目: 具备 T 容量的背包,要装 N 个商品,每个商品都有对应的重量 w 和价值 v ,如何保证背包在最大容量下装入的商品具备最大价值 ? 在开始计算前,先列出一个假设图表,方便理解:

image.png

Q: 具备 T 容量的背包,要装 N 个商品,每个商品都有对应的重量 w 和价值 v ,如何背包在最大容量下装入的商品具备最大价值 ? 定义一个假设列表,寻找列表的规律: image.png tip: 第一列假设容量T=0 和 第一行假设物品信息都是0的情况没有太大意义,因为什么都放不了肯定这一行最大价值都是0,但目的为了方便第几个后面列表索引是从0开始传入入参。这个初始化表格可看成一个二维数组👇 dp,初始化后我们开始逐个填写最大价值替换默认0

const dp =[ [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0] ]

①当假设物品数量 N=1, 物品重量w1 = 2,物品价值v1 = 1 时:dp[N][T] 如下 image.png 假设 容量T=0时,不够放w1,dp[1][0] = 0; 假设 容量T=1时,不够放w1,dp[1][1] = 0; 假设 容量T=2时,够放w1=2,dp[1][2] = v1,所以 dp[1][2] = 1; T=3 、 T=4 以此类推 都够放w1, dp[N][T] = 1。 N=1时,只有一个物品,所以只要够放背包就直接设置其价值,

所以第一行时**【公式1】**为:

if(w1>T){
    dp[N][T] = 0
}else { //
    dp[N][T] = v1
}

②当假设物品数量 N=2, 加入了物品重量w2 = 1,物品价值v2 = 3时:dp[N][T] 如下: image.png 假设 容量T=0时,不够放w1、w2,dp[2][0] = 0; 假设 容量T=1时,够放w2=1, 及 w2 <= T,但是需要比较w1 \ w2价值,比较方式可采用与上一层及为只有1个商品时的已计算过的情况进行比较, 其他行的【公式2】为:

Math.max(
上一层N-1时同容量T最大价值dp[N-1][T] ,
(当前层物品价值v2) + (上一层容量T=2 - 这一层用了的重量w2的情况下的最大价值) ,为 v2 + dp[N -1][T - w2]
)

同理:假设 容量T=2时,够放w2 或者 w1,及w2 < T,但是需要比较w1 \ w2价值,比较方式可采用与上一层及为只有1个商品时的已计算过的情况进行比较,伪代码公式为:

Math.max(
上一层N-1时同容量T最大价值dp[2-1][2] ,
(当前层物品价值v2) + (上一层容量T=2 - 这一层用了的重量w2的情况下的最大价值) ,为 v2 + dp[2-1][2 - w2]
)

T=3 、T=4 计算方式与上面一致。

③当假设物品数量 N=3, 加入了物品重量w3 = 3,物品价值v3 = 2时:dp[N][T] 如下: image.png 假设 容量T=0时,不够放w1、w2、w3,dp[3][0] = 0; 假设 容量T=1时,不够放w3,及w3>T,但是我们上一层N-1已经完成了比较,直接取上一层 N-1 同T情况比较过的值来用就行, 所以当当前行物品w3 > 背包容量T,

则【公式3】为:dp[N][T] = dp[N-1][T] 到此,已经可以根据相同的规则来抒写计算公式来填充这个表格。

image.png

可以看出,只要我们通过循环行N,再循环每个T进行判断负值,这个表格的价值填充完毕后最大价值dp[N][T] 就是表格最后一个值,就计算出来了,下面是实现方法:

 window.onload = function(){
        // 背包总共可承重量
        const T = 7;
        // 商品数量
        const N = 4;
        // 商品重量 集合 ,wList[1] 对应第1个物品的重量
        const wList = [2, 1, 3,4]
        // 商品价值 集合, vList[1] 对应第1个物品的价值
        const vList = [1,3, 2,4]
        console.log(getMaxValue(T,N,wList,vList))
        function getMaxValue(T,N,wList,vList){
            // 定义出我们的dp价值二维数组
            let dpInit = new Array(N+1).fill(0).map(()=>Array(T+1).fill(0))
            let dp = JSON.parse(JSON.stringify(dpInit))
            console.log('初始化dp=',dpInit)
            for (let x = 1; x <= N; x++) { // 循环商品可放顺序 
                for (let y = 1; y <= T; y++) { // 循环假设的背包总可承重
                    if(x===1){ // 第一行时 取值不想上取
                        if(wList[x-1] > y) { // 当前物品重量 > 可承载量 , 啥都放不了
                            dp[x][y] = 0;
                        }else { // 背包够放这个物品,总价值dp[1][y] = 这个物品的价值 vList[x]
                            dp[x][y]= vList[x-1]
                        }
                    }else { // 第二行开始,
                        if(wList[x-1] > y) { // 当前物品重量 > 可承载量 , 啥都放不了
                            dp[x][y] = dp[x-1][y];
                        }else { // 背包够放这个物品,,总价值dp[x][y] = 上一层x,具备y的容量 
                      //  或者 当前这一层物品x的价值 vList[x] + [上一层开始计算物品] [具备的容量y - 当前用了的重量wList[x]]] 
                            dp[x][y] = Math.max(dp[x-1][y],vList[x-1] + dp[x-1][y-wList[x-1]])
                        }
                    }
                }
            }
            console.log('结果dp=',dp)
            console.log('具备'+N+'个物品时'+', '+'背包承重='+T+' 下的最大价值👇')
            console.log(dp[N][T])
            //  返回最大情况的 物品数量N 和 最大背包承重 的价值,就是最大价值。
            return dp[N][T];
        }
    }

今天,你学废了吗?