01背包问题的变式

本文通过实例解析01背包问题的特点,介绍了如何利用动态规划方法解决此类问题,包括确定最大容量、构建递推公式并进行剪枝优化,以求解例题1、2和3中的问题.
摘要由CSDN通过智能技术生成

先说一下01背包问题的特点:
首先就是该背包中的物品有且只有一项,也就意味着无法重复往背包里加同一个物品
dp数组的下标表示容量,值表示最终的重量
例题1:1049最后一块石头的重量II
做着一类题最先需要找到的是最大容量是多少?
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
那么容量就是Math.floor(sum / 2)
那么接下来就是直接套公式就行了

/**
 * @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];
};

例题2:494目标和
本题个人认为是一道hard的题目,作为中等题应该是因为题意比较好懂
同样也是先找容量代表谁,如果用left表示带+的数量,right反之,就会有如下公式:
left + right = sum
left-right=target
联立不难得出
left = (target + sum)/2
left就是最后的容量
求完容量就来到了本题的第二个难点:
如何递推,根据题意的要求是求指定容量下组合方式数量,那就是累加
dp[j] += dp[j - nums[i]]
接下来套公式就行了
完整代码:

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

注意一些剪枝操作避免出现报错

例题3:474一和零
这一题的关键在于容量有两个但是并不难找
递推公式的关键是加一
完整代码:

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、付费专栏及课程。

余额充值