416. 分割等和子集(dp01背包,深刻理解动态规划)

117 篇文章 0 订阅
48 篇文章 1 订阅

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。


示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5][11] 。

示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

【思路】:
本题思路非常巧妙,将其转成01背包的动态规划。

因为要求是分成两个等和子集,所以其实我们可以理解为,是否可以从输入数组中挑选出一些正整数,使得这些数的和 等于 整个数组元素的和的一半。

因此,我们定义dp[i][val] 表示前i个物品,能否凑出重量为val的这种方案。

如果dp[i][val] = 1表示可以凑出,等于0表示不能凑出。

状态转移:基本上和01背包一样的思想。

dp[i][val]=Math.max(dp[i - 1][val], dp[i - 1][val - nums[i]])

【代码】:
以下代码保留详细注释:

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    let sum = 0;
    let target = 0;
    for(let i = 0;i < nums.length;i++){
        sum += nums[i];
    }
    if(sum % 2 !=0 )    return false;
    target = Number.parseInt(sum / 2);
    //二维dp
    var dp = Array(nums.length).fill().map(() => Array(sum+1).fill(0));   
    //01背包  dp[i][val]=Math.max(dp[i - 1][val], dp[i - 1][val - nums[i]]);       dp数组只存0和1. 如果能产生此种价值方案就存1,不能就存0
    
    //第一行,只有一个物品时能拿两种价值方案。
    dp[0][0] = 1;   //不拿
    dp[0][nums[0]] = 1; //拿第一个物品
    
    //第二行,有两个物品。找到规律。所以从第二行开始就可以套用状态转移了
    // dp[1][0] = 1;    
    // dp[1][nums[0]] = 1;
    // dp[1][nums[0] + nums[1]] = 1;
    // dp[1][nums[1]] = 1;

    for(let i = 1;i < nums.length;i++){
        //枚举每一种可能产生的方案。容量枚举只需要到target就够了
        //再往后枚举没有意义
        for(let j = 0;j <= target;j++){
            if(j - nums[i] >= 0)
                dp[i][j] = Math.max(dp[i - 1][j], dp[i-1][j-nums[i]]);
            else
                dp[i][j] = dp[i-1][j]
        }
    } 
    return dp[nums.length - 1][target] == 1;
};

经过本题之后,确实感觉对动态规划更有感觉了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值