打卡第35天------动态规划

我现在每天都在用从代码随想录学到的方法去尝试自己解题,希望算法能力可以尽快提上去。骑马找马,一边工作一边找工作,希望可以尽快成功上岸。每天都在这样不停的祈祷上帝。

一、最后一块石头的重量II

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

题目描述:

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

解题思路大概了解一下吧:

将石头尽可能的分成两堆。相撞之后就是最小值了。重量总和近似相等的两堆。

每个物品只能用一次,0-1背包问题

动归五部曲:

1、dp[i],容量j,最大价值为dp[j];对于本体来说,价值就是重量,重量就是价值。

2、递推公式:

dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])

对于本题:

dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i])

3、初始化

dp[0] = 0;

dp[1501] = 0;

4、遍历顺序

第一层i遍历物品

第二层j遍历背包,从大往小遍历,target = Math.floor(sum / 2) ;

5、打印dp数组

结果为:sum - dp[target] - dp[target] ;

完整版的代码:

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

二、目标和

leetcode题目链接:494.目标和

题目描述:

给你一个非负整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

注意看一下代码:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
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];
};

三、一和零

leetcode题目链接:474.一和零

题目描述:

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

二维dp数组,有三个变量

1、确定dp数组的含义

装满i个0,j个1,最多背了多少个物品,dp[i][j];即dp[m][n]。

2、确定递推公式

 纯零一背包的递推公式:dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i])

假设物品里面有x个0,y个1.

本题的递推公式:dp[i][j] = Math.max(dp[i - x][j - y] + 1, dp[i][j])

3、初始化

dp[0][0] = 0;非零下标也都初始成0;

4、遍历顺序

先物品,后背包,背包要倒序遍历。

5、打印dp数组

来看一下具体的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];
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值