代码随想录第四十二天 动态规划 Part4 ● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集

  •  01背包问题,你该了解这些!

题目链接:46. 携带研究材料(第六期模拟笔试)

背包问题:dp[i][j]表示从 0-i 的物品中任意取,放进容量为 j 的背包,最大的价值和;

看了解析后总结背包问题的思路:

1)含义:dp[i][j]表示从 0-i 的物品中任意取,放进容量为 j 的背包,最大的价值和; 遍历 0-i 的物品,判断放入该物品的最大值, 遍历所有的背包容量情况(j), 判断当前 dp[i][j[的值如果 当前背包的容量小于物品的重量。则不放入第 i 个物品

2)递推公式: 

如果不放入第 i 个物品,则 dp[i][j] = dp[i - 1][j];

如果放入第 i 个物品,那 dp[i][j] = dp[i - 1][j - weight[i]] + value[i]

3)初始化

dp[0][j]> weight[0]的时候,也就是第一个物品能被放下的时候,最大值就取第一个物品的价值,因为每个物品只能放一次,所以之后的初始化的值也是这个第一个物品的价值

4)递推方法,这里先遍历物品,再遍历容量;其实都可以

代码实现:

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const inputLines = [];

rl.on('line', (line) => {
  inputLines.push(line);
  if (inputLines.length === 3) {
    rl.close();
  }
});

rl.on('close', () => {
  processInput(inputLines);
});


function materialChoose(weight, value, size) {
    let len = weight.length
    let dp = new Array(len).fill().map(() => new Array(size+1).fill(0))
    // i是第 i 个材料,j 是背包的容量
    for(let j = weight[0]; j <= size; j = j +1) {
        dp[0][j] = value[0]
    }
    for(let i =1; i < len; i = i + 1) {
        for(let j = 0; j <= size; j = j + 1) {
            if(j < weight[i]) dp[i][j] = dp[i - 1][j]
            else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
        }
    }
    return dp[len - 1][size]
}

function processInput(lines) {
    let newArray = []
    lines.forEach((line) => {
        newArray.push(line.split(' ').map(item => Number(item)))
    })
    console.log(materialChoose(newArray[1], newArray[2], newArray[0][1]))
}
  •  01背包问题,你该了解这些! 滚动数组  

题目链接:46. 携带研究材料(第六期模拟笔试)和上题相同

思路:dp可以由 dp[i][j]变成 dp[j],把 dp[i- 1] 拷贝到 dp [i]层,表达式完全可以是 dp[i][j] = max(dp[j], dp[j - weight[i]] + value[i]),;

为什么要倒序?因为下一个容量依赖上一个容量,如果不是倒序的话,会多次算进上一个容量的最大值;

为什么需要先遍历物品?因为一个物品只能放进去一次

代码如下:

function materialChoose(weight, value, size) {
    let len = weight.length
    let dp = new Array(size + 1).fill(0)
    for(let i = 0; i < weight.length; i = i + 1) {
        for(let j = size; j >= weight[i]; j = j - 1) {
            dp[j] = Math.max(dp[j - weight[i]] + value[i], dp[j])
        }
    }
    console.log(dp)
    return dp[size]
}
  •  416. 分割等和子集 

题目链接:. - 力扣(LeetCode)

首先如果要等和,这个和肯定是所有数的 sum/2

然后要将将这个和为 sum/2 的背包装满;

用一维滚动数组来解决:

代码如下:

var canPartition = function(nums) {
    let sum = nums.reduce((accu, curr) => accu + curr, 0) / 2

    if(sum % 1 !== 0) {
        return false
    }

    let dp = new Array(sum + 1).fill(0)

    for(let i = 0; i < nums.length; i = i + 1) {
        for(j = sum; j >= nums[i]; j = j - 1 ) {
            dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i])          
        }
    }
    return dp[sum] === sum
};

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值