代码随想录第36天 | 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

1049. 最后一块石头的重量

第一想法

js 复制代码
/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeightII = function (nums) {

    // 和分割两个和相等的子数组一样
    //dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
    //value[i]和weight[i] 数值
    // 结果,用来sum-dp[j]*2的最小值
    let sum = nums.reduce((x, y) => x + y)
    let dp = new Array(sum).fill(0);
    // 开始 01背包
    for (let i = 0; i < nums.length; i++) {
        for (let j = sum; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
            dp[j] = dp[j] > dp[j - nums[i]] + nums[i] ? dp[j] : dp[j - nums[i]] + nums[i]
        }
    }
    let i = 0
    let a = 100000000
    while (i < dp.length) {
        a = Math.abs(sum - dp[i] * 2) > a ? a : Math.abs(sum - dp[i] * 2)
        i++
    }
    return a
};

优化

复制代码
不用算到最后面,就写到一般就好了


但js 中 n/a不会取整

用Math.floor(sum / 2);

js 复制代码
/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeightII = function(nums) {

 let sum=nums.reduce((x,y)=>x+y)
  let dpLen = Math.floor(sum / 2);
     let dp = new Array(dpLen+1).fill(0);
for(let i = 0; i < nums.length; i++) {
    for(let j = dpLen; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
        dp[j] = dp[j]> dp[j - nums[i]] + nums[i]? dp[j]:dp[j - nums[i]] + nums[i]
    }
}
return sum-dp[dpLen]*2
};

494. 目标和

js 复制代码
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var findTargetSumWays = function(nums, target) {
  let sum=nums.reduce((x,y)=>x+y)
  if((sum+target)%2) return 0
  if(Math.abs(target)>sum) return 0
  const hf=(sum+target)/2
  let dp = new Array(hf+1).fill(0);
  dp[0]=1
  for (let i = 0; i < nums.length; i++) {
        for (let j =hf; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
         dp[j] += dp[j - nums[i]]
        }
    }
    return dp[hf]
};

第一想法

排列组合

困难

  • 问题转换

假设加法的总和为x,那么减法对应的总和就是sum - x。

所以我们要求的是 x - (sum - x) = target

x = (target + sum) / 2

此时问题就转化为,装满容量为x的背包,有几种方法。

  • dpj += dpj - nums\[i]

总结

  • dpj = max(dpj, dpj - nums\[i] + numsi);
    一个限重的包装石头,石头有重量,有价值,怎样装最值钱
  • dpj += dpj - nums\[i]
    dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法

一和零

js 复制代码
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];
};

思路

  1. dpij:最多有i个0和j个1的strs的最大子集的大小为dpij

  2. dpij 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

    dpij 就可以是 dpi - zeroNumj - oneNum + 1。

    然后我们在遍历的过程中,取dpij的最大值。

    所以递推公式:dpij = max(dpij, dpi - zeroNumj - oneNum + 1);

    此时大家可以回想一下01背包的递推公式:dpj = max(dpj, dpj - weight\[i] + valuei);

    对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weighti),字符串本身的个数相当于物品的价值(valuei)。

这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。

相关推荐
小陈的代码之路6 小时前
回文链表(LeetCode 234)C语言最佳解题思路
c语言·leetcode·链表
郭梧悠9 小时前
算法:有效的括号
python·算法·leetcode
旖-旎9 小时前
《LeetCode 1137 第N个泰波那契数 和 LeetCode 三步问题》
c++·算法·leetcode·动态规划
wabs6669 小时前
关于动态规划【力扣718.最长重复子数组的思考】
算法·leetcode·动态规划
YuK.W11 小时前
Leetcode100: 94.二叉树中序遍历、104.二叉树最大深度、226.翻转二叉树
java·算法·leetcode·二叉树
想吃火锅100517 小时前
【leetcode】146.LRU缓存js
算法·leetcode·缓存
To_OC6 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC6 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
To_OC7 天前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
To_OC7 天前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode