Day35-动态规划,leetcode416

01背包问题

  • 描述:有n种物品,每种物品只有1个,每个物品有自己的重量会有自己的价值,有一个最多只能放重量为m的背包,问:这个背包最多能装价值为多少的物品?尽可能的往背包里装,这个背包最多能装的价值是多少?

  1. 携带研究材料(卡码网)
  • 小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。

  • 小明的行李空间为 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]);
});



  1. 分割等和子集
  • 给你一个 只包含正整数 的 非空 数组 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;
};



参考&感谢各路大神

posted @ 2025-07-01 09:04  安静的嘶吼  阅读(8)  评论(0)    收藏  举报