Day35-动态规划,leetcode416
01背包问题
- 描述:有n种物品,每种物品只有1个,每个物品有自己的重量会有自己的价值,有一个最多只能放重量为m的背包,问:这个背包最多能装价值为多少的物品?尽可能的往背包里装,这个背包最多能装的价值是多少?
- 携带研究材料(卡码网)
-
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
-
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。
-
输入描述
-
第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。
-
第二行包含 M 个正整数,代表每种研究材料的所占空间。
-
第三行包含 M 个正整数,代表每种研究材料的价值。
-
输出描述
-
输出一个整数,代表小明能够携带的研究材料的最大价值。
- 思路
- 二维数组解决背包问题
- 1.确定dp数组定义及下标含义:,i 、j、dp[i][j],i 来表示物品、j表示背包容量、dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
- 2.确定递推公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
- 3.dp数组如何初始化,关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。由递推公式值,i是由i-1推导出来,那么i=0时要初始化。如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。dp[i][0] = 0,dp[0][j] = value[0]
- 4.遍历顺序,先遍历物品,然后遍历背包重量
- 5.dp数组
/**
* 01背包问题的标准动态规划解法,适用于“每种物品只能选一次,求最大价值”的场景
*/
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
readline.on('line', (line) => {
input.push(line);
});
readline.on('close', () => {
// 输入处理
// n: 物品种类数,bagweight: 背包容量
let [n, bagweight] = input[0].split(' ').map(Number);
// 各物品重量
let weight = input[1].split(' ').map(Number);
// 各物品价值
let value = input[2].split(' ').map(Number);
// dp数组定义,dp[i][j] 表示:只考虑前 i 个物品(下标0~i),在背包容量为 j 时能获得的最大价值。
let dp = Array.from({ length: n }, () => Array(bagweight + 1).fill(0));
// 初始化,只放第0个物品,如果容量够(j >= weight[0]),最大价值就是 value[0],否则为0。
for (let j = weight[0]; j <= bagweight; j++) {
dp[0][j] = value[0];
}
for (let i = 1; i < n; i++) {
for (let j = 0; j <= bagweight; j++) {
// 装不下第i个物品,只能继承上一个状态
if (j < weight[i]) {
dp[i][j] = dp[i - 1][j];
} else {
// 不选第i个物品 or 选第i个物品,取最大值
// 不选第i个物品:价值是 dp[i-1][j]
// 选第i个物品:价值是 dp[i-1][j-weight[i]] + value[i]
// 取两者最大值
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
// 输出考虑所有物品、容量为 bagweight 时的最大价值
console.log(dp[n - 1][bagweight]);
});
- 1.确定dp数组定义及下标含义: 一维数组dp[j],容量为j的背包,所背的物品价值的最大价值为dp[j]
- 2.递归公式: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
- 3.dp数组如何初始化,假设物品价值都是大于0的,所以dp数组初始化的时候,都初始为0
- 4.遍历顺序,倒序遍历是为了保证物品i只被放入一次
- 5.dp数组
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
readline.on('line', (line) => {
input.push(line);
});
readline.on('close', () => {
// 读取 M 和 N
let [M, N] = input[0].split(' ').map(Number);
let costs = input[1].split(' ').map(Number);
let values = input[2].split(' ').map(Number);
// 创建一个动态规划数组dp,初始值为0
let dp = new Array(N + 1).fill(0);
// 外层循环遍历每个类型的研究材料
for (let i = 0; i < M; ++i) {
// 内层循环从 N 空间逐渐减少到当前研究材料所占空间
for (let j = N; j >= costs[i]; --j) {
// 考虑当前研究材料选择和不选择的情况,选择最大值
dp[j] = Math.max(dp[j], dp[j - costs[i]] + values[i]);
}
}
// 输出dp[N],即在给定 N 行李空间可以携带的研究材料最大价值
console.log(dp[N]);
});
- 分割等和子集
- 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
- 思路
- 01背包应用,物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2
- 1.确定dp数组定义及下标含义: 一维数组dp[j],容量为j的背包,所背的物品价值的最大价值为dp[j]
- 2.递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
- 3.dp数组如何初始化,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。从dp[j]的定义来看,dp[0]=0
- 4.遍历顺序,如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历,倒序遍历是为了保证物品i只被放入一次
- 5.dp数组:dp[j]的数值一定是小于等于j的。如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j
var canPartition = function(nums) {
let sum = nums.reduce((a, b) => a + b, 0);
if (sum % 2 !== 0) return false; // 如果总和为奇数,无法平分
let target = sum / 2;
let dp = new Array(target + 1).fill(0);
// 01背包
for (let i = 0; i < nums.length; i++) {
for (let j = target; j >= nums[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
// 在内层循环中,如果 dp[j] === sum / 2,就直接 return true;,即一旦找到一个子集和等于目标值就提前返回,不再继续遍历。
if (dp[j] === target) {
return true;
}
}
}
return dp[target] === target;
};
参考&感谢各路大神
宝剑锋从磨砺出,梅花香自苦寒来。

浙公网安备 33010602011771号