【动态规划】1049. 最后一块石头的重量 II、494. 目标和、474. 一和零

提示:努力生活,开心、快乐的一天


1049. 最后一块石头的重量 II

题目链接:1049. 最后一块石头的重量 II

💡解题思路

  1. 本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
  2. 动规五部曲:
  • 确定dp数组以及下标的含义:dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。
  • 确定递推公式:01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
  • dp数组如何初始化:因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。
  • 确定遍历顺序:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
  • 举例推导dp数组:按照递推公式推导一下做推导,如果发现结果不对,就把dp数组打印出来在这里插入图片描述
    分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。
    在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的。
    那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]

🤔遇到的问题

  1. 分两堆石头的时候,没有向下取整

💻代码实现

动态规划

var lastStoneWeightII = function (stones) {
    let sum = stones.reduce((a, b) => a + b)
    //向下取整,总和为5,取2,所以sum-dp[dplen]>=dp[dpLen]
    let dpLen = Math.floor(sum / 2)
    let dp = new Array(dpLen + 1).fill(0)
    for (let i = 0; i < stones.length; i++) {
        for (let j = dpLen; j >= stones[i]; --j) {
            dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i])
        }
    }
    //两个相近的集合求查差值
    return sum - dp[dpLen] - dp[dpLen]
};

🎯题目总结

本题其实和416. 分割等和子集 (opens new window)几乎是一样的,只是最后对dp[target]的处理方式不同。
416. 分割等和子集 (opens new window)相当于是求背包是否正好装满,而本题是求背包最多能装多少。

494. 目标和

题目链接:494. 目标和

💡解题思路

  1. 因为每个物品(题目中的1)只用一次!
    这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。
  2. 假设加法的总和为x,那么减法对应的总和就是sum - x。
    所以我们要求的是 x - (sum - x) = target
    x = (target + sum) / 2
    此时问题就转化为,装满容量为x的背包,有几种方法。
    这里的x,就是bagSize,也就是我们后面要求的背包容量。
    (target + sum) / 2 不能有余数,有余数的话,算是无解
  3. 动规五部曲:
  • 确定dp数组以及下标的含义:dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
  • 确定递推公式:只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。例如:dp[j],j 为5,
    1、已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
    2、已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
    3、已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
    4、已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
    5、已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
    那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
    递推公式为:dp[j] += dp[j - nums[i]]
  • dp数组如何初始化: dp[0] 为 1
  • 确定遍历顺序:01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
  • 举例推导dp数组:按照递推公式推导一下做推导,如果发现结果不对,就把dp数组打印出来在这里插入图片描述

🤔遇到的问题

  1. 递推公式的获取非常的难思考到

💻代码实现

动态规划

var findTargetSumWays = function(nums, target) {
    const sum = nums.reduce((a, b) => a+b);
    if(Math.abs(target) > sum) {
        return 0;
    }
    if((target + sum) % 2) {
        return 0;
    }
    const halfSum = (target + sum) / 2;
    let dp = new Array(halfSum+1).fill(0);
    dp[0] = 1;
    for(let i = 0; i < nums.length; i++) {
        for(let j = halfSum; j >= nums[i]; j--) {
            dp[j] += dp[j - nums[i]];
        }
    }
    return dp[halfSum];
};

🎯题目总结

本题还是有点难度,大家也可以记住,在求装满背包有几种方法的情况下,递推公式一般为:
dp[j] += dp[j - nums[i]];


474. 一和零

题目链接:474. 一和零

💡解题思路

  1. 依旧是01背包问题
  2. 动规五部曲:
  • 确定dp数组以及下标的含义:dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
  • 确定递推公式:dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
    dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
    然后我们在遍历的过程中,取dp[i][j]的最大值。
    所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1); - dp数组如何初始化: dp[0][0] 为 0
  • 确定遍历顺序:01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
  • 举例推导dp数组:按照递推公式推导一下做推导,如果发现结果不对,就把dp数组打印出来在这里插入图片描述

🤔遇到的问题

  1. 容易搞混i,j的含义,m,n为两个背包

💻代码实现

动态规划

var findMaxForm = function(strs, m, n) {
    const dp = Array.from(Array(m+1), () => Array(n+1).fill(0));
    let numOfZeros, numOfOnes;

    for(let str of strs) {//遍历物品(字符串),统计0,1的个数
        numOfZeros = 0;
        numOfOnes = 0;
        for(let c of str) {
            if (c === '0') {
                numOfZeros++;
            } else {
                numOfOnes++;
            }
        }
        for(let i = m; i >= numOfZeros; i--) {//遍历书包容量(从后向前),0,1的个数就是背包的容量
            for(let j = n; j >= numOfOnes; j--) {
                dp[i][j] = Math.max(dp[i][j], dp[i - numOfZeros][j - numOfOnes] + 1);
            }
        }
    }
    return dp[m][n];
};

🎯题目总结

01背包问题大汇总

  • 纯 0 - 1 背包 (opens new window)是求 给定背包容量 装满背包 的最大价值是多少。
    1. 分割等和子集 (opens new window)是求 给定背包容量,能不能装满这个背包。
    1. 最后一块石头的重量 II (opens new window)是求 给定背包容量,尽可能装,最多能装多少
    1. 目标和 (opens new window)是求 给定背包容量,装满背包有多少种方法。
  • 本题是求 给定背包容量,装满背包最多有多少个物品。

🎈今日心得

背包问题的拓展,好难

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值