【编程题】动态规划之01背包问题

问题描述

现有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?

代码实现 

function knapsack(capacity,weight,value){
    // 初始化一个 weight.length+1行 X capacity+1列 的二维数组
    let dp = [];
    for(let i=0; i<weight.length+1; i++){
        dp[i] = [];
        for(let j=0; j<capacity+1; j++){
            dp[i][j] = 0;
        }
    }

    // dp[i][j]表示当前背包容量为j,前i个物品最佳组合对应的价值
    for(let i=1; i<weight.length+1; i++){
        for(let j=1; j<capacity+1; j++){
            if(j < weight[i-1]){
                dp[i][j] = dp[i-1][j];
            } else {
                dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1])
            }
        }
    }

    // 最后一个值即为最大的价值总和
    return dp[weight.length][capacity];
}

// 测试
let capacity = 8;
let weight = [2,3,4,5];
let value = [3,4,5,6];
console.log(knapsack(capacity,weight,value));

背包问题最优解回溯

通过上面的方法可以求出背包问题的最优解,但还不知道这个最优解由哪些商品组成,故要根据最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:

  • dp(i,j)=dp(i-1,j)时,说明没有选择第 i 个商品,则回到dp(i-1,j);
  • dp(i,j)=dp(i-1,j-weight(i-1))+value(i-1)时,说明装了第 i 个商品,该商品是最优解组成的一部分,随后我们得回到装该商品之前,即回到dp(i-1,j-weight(i-1)); 
  • 一直遍历到 i=0 结束为止,所有解的组成都会找到。

代码实现

function knapsack(capacity,weight,value){
    // 初始化一个 weight.length+1行 X capacity+1列 的二维数组
    let dp = [];
    for(let i=0; i<weight.length+1; i++){
        dp[i] = [];
        for(let j=0; j<capacity+1; j++){
            dp[i][j] = 0;
        }
    }

    // dp[i][j]表示当前背包容量为j,前i个物品最佳组合对应的价值
    for(let i=1; i<weight.length+1; i++){
        for(let j=1; j<capacity+1; j++){
            if(j < weight[i-1]){
                dp[i][j] = dp[i-1][j];
            } else {
                dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1])
            }
        }
    }

    // 最后一个值即为最大的价值总和
    console.log('最大价值为:', dp[weight.length][capacity]);
    
    // 定义一个数组,长度为weight.length; 其中第i个数(下标为i-1)表示第i个商品是否被选中,选中为1,未选中为0
    let item = [];

    function detail(i,j){
        if(i>0){
            if(dp[i][j] === dp[i-1][j]){
                item[i-1] = 0;
                detail(i-1,j);
            } else if(j-weight[i-1] >= 0 && dp[i][j] === dp[i-1][j-weight[i-1]]+value[i-1]) {
                item[i-1] = 1;
                detail(i-1,j-weight[i-1]);
            }
        }
        return item;
    }

    console.log('商品组成:', detail(weight.length,capacity));
}

// 测试
let capacity = 8;
let weight = [2,3,4,5];
let value = [3,4,5,6];
knapsack(capacity,weight,value);
// 最大价值为: 10
// 商品组成: [ 0, 1, 0, 1 ]

参考:【动态规划】01背包问题(通俗易懂,超基础讲解)

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值