一、完全背包
有n件物品,每件物品有无限多个,一个最多能背W的背包,第i件物品的重量为weight[i],价值为value[i],求将哪些物品放入背包,得到的价值总和最大
01背包和完全背包的唯一不同就在于遍历顺序
01背包的核心代码
for(let i = 0; i <= weight.length;i++) { // 遍历物品
for(let j = bagWeight; j >= weight[i];j--) { // 遍历背包容量
dp[j] = Math.max(dp[j], dp[j - weight[i]] + weight[i])
}
}
完全背包的物品可以多次添加,所以需要从小到大正序遍历
for(let i = 0; i <= weight.length;i++) { // 遍历物品
for(let j = weight[i]; j <= bagWeight ;j++) { // 遍历背包容量
dp[j] = Math.max(dp[j], dp[j - weight[i]] + weight[i])
}
}
对于01背包,二维dp数组,两个for循环嵌套顺序是无所谓的,一维dp数组的两个for循环必须是先遍历物品,后遍历背包
对于完全背包,一维dp,两个for循环嵌套顺序是无所谓的,只要保证下标j之前的dp[j]都是经过计算的就可以
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const inputArr = [];//存放输入的数据
rl.on('line', function(line){
//line是输入的每一行,为字符串格式
inputArr.push(line.split(' '));//将输入流保存到inputArr中(注意为字符串数组)
}).on('close', function(){
console.log(fun(inputArr))//调用函数并输出
})
function fun(inputArr) {
let [M, N] = inputArr[0].map(item => Number(item))
let weight = []
let value = []
for(let [w, v] of inputArr.slice(1)) {
weight.push(Number(w))
value.push(Number(v))
}
let dp = new Array(N+1).fill(0)
for(let i = 0; i < M; i++) {
for(let j = weight[i]; j <= N;j++) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])
}
}
return dp[N]
}
二维dp
function fun(inputArr) {
let [M, N] = inputArr[0].map(item => Number(item))
let weight = []
let value = []
for(let [w, v] of inputArr.slice(1)) {
weight.push(Number(w))
value.push(Number(v))
}
let dp = new Array(M).fill(0).map(_ => new Array(N + 1).fill(0))
for (let j = weight[0]; j <= N; j++) {
dp[0][j] = value[0]
}
for (let i = 1; i < M; i++) {
for (let j = 0; j <= N; 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][j - weight[i-1]] + value[i-1])
}
}
}
return dp[M - 1][N]
}
二、零钱兑换2
求凑成总金额的物品组合个数
五部曲
dp[j],凑成总金额j的组合数- 确定递推公式,
dp[j] += dp[j - coins[i]] - dp初始化,
dp[0] = 1,递归的基础,后序累加依赖于此,其他初始化为0,不影响累计 - 遍历顺序,必须是先外层循环钱币,内层循环金钱总额,否则,不同金额算出来是排列数
- 举例
/**
* @param {number} amount
* @param {number[]} coins
* @return {number}
*/
var change = function(amount, coins) {
let dp = new Array(amount + 1).fill(0)
dp[0] = 1
for(let i = 0; i < coins.length; i++) {
for(let j = coins[i]; j <= amount;j++) {
dp[j] += dp[j - coins[i]]
}
}
return dp[amount]
};
三、组合总和4
本题其实是求排列
和上题的区别就在于遍历顺序,本题先外层遍历背包,内层遍历物品
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var combinationSum4 = function(nums, target) {
let dp = new Array(target + 1).fill(0)
dp[0] = 1
for(let i = 1; i <= target; i++) {
for(let j = 0; j < nums.length;j++) {
if(i >= nums[j]) {
dp[i] += dp[i - nums[j]]
}
}
}
return dp[target]
};