代码随想录算法训练营第四十五天| 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

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

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

文档讲解:代码随想录/最后一块石头的重量 II

视频讲解:视频讲解-最后一块石头的重量 II

状态:已完成(1遍)

解题过程 

这几天属实是有点分身乏术了,先直接看题解AC了,二刷的时候再来补上自己的思路和尝试吧。

看完代码随想录之后的想法 

用动态规划五部曲:

  1. 确定dp数组以及下标的含义:dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j];
  2. 确定递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
  3. 代码初始化如下:因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖;

  4. 确定遍历顺序:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历;
  5. 举例推导dp数组:

讲解代码如下:

/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeightII = function (stones) {
    let sum = stones.reduce((s, n) => s + n);

    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];
};


 494. 目标和

题目链接:494. 目标和

文档讲解:代码随想录/目标和

视频讲解:视频讲解-目标和

状态:已完成(1遍)

解题过程  

 看完代码随想录之后的想法 

用动态规划五部曲:

  1. 确定dp数组以及下标的含义:填满j(包括j)这么大容积的包,有dp[j]种方法
  2. 确定递推公式:
    dp[j] += dp[j - nums[i]]
  3. 代码初始化如下:

    从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

    这里有录友可能认为从dp数组定义来说 dp[0] 应该是0,也有录友认为dp[0]应该是1。

    其实不要硬去解释它的含义,咱就把 dp[0]的情况带入本题看看应该等于多少。

    如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。

    所以本题我们应该初始化 dp[0] 为 1。

    可能有同学想了,那 如果是 数组[0,0,0,0,0] target = 0 呢。

    其实 此时最终的dp[0] = 32,也就是这五个零 子集的所有组合情况,但此dp[0]非彼dp[0],dp[0]能算出32,其基础是因为dp[0] = 1 累加起来的。

    dp[j]其他下标对应的数值也应该初始化为0,从递推公式也可以看出,dp[j]要保证是0的初始值,才能正确的由dp[j - nums[i]]推导出来。

  4. 确定遍历顺序:

    nums放在外循环,target在内循环,且内循环倒序

  5. 举例推导dp数组

讲解代码如下:

const findTargetSumWays = (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];
};


474.一和零

题目链接:474.一和零

文档讲解:代码随想录/一和零

视频讲解:视频讲解-一和零

状态:已完成(1遍)

解题过程  

看完代码随想录之后的想法 

用动态规划五部曲:

  1. 确定dp数组以及下标的含义:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
  2. 确定递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
  3. dp数组如何初始化:

    01背包的dp数组初始化为0就可以。

    因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖;

  4. 确定遍历顺序:外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历;
  5. 举例推导dp数组:

    按照这个递推公式我们来推导一下,dp数组应该是如下的数列: 10 15 30  。

讲解代码如下:

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

    for(let str of strs) {
        numOfZeros = 0;
        numOfOnes = 0;
    
        for(let c of str) {
            if (c === '0') {
                numOfZeros++;
            } else {
                numOfOnes++;
            }
        }

        for(let i = m; i >= numOfZeros; i--) {
            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];
};

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值