【leetcode】分割等和子集

题目:给定一个非空的正整数数组 nums ,请判断能否将这些数字分成元素和相等的两部分。
示例:

输入:nums = [1,5,11,5]
输出:true
解释:nums 可以分割成 [1, 5, 5][11] 。
输入:nums = [1,2,3,5]
输出:false
解释:nums 不可以分为和相等的两部分

题解:
转化成0-1背包问题,背包容量为sum/2,dp[i][j]表示从前i个物品中能刚好将背包容量为j塞满,
状态转移方程:① 若j-nums[i]>0,说明背包能放下物品i,这时可以选择将物品i放入背包或者不放入

  • 不放入,则dp[i][j]=dp[i-1][j]表示当前容量为j的背包只考虑在前i-1个物品中选择
  • 放入,则dp[i][j]=dp[i-1][j-nums[i]],则i-1个物品所能放入的背包容量就是j-nums[i],如果容量为j的背包放入物品i后刚好放满,则说明dp[i-1][j-nums[i]]也能刚好放满,如果不能放满,则dp[i-1][j-nums[i]]也不能放满
    ② j-nums[i]<0,背包不能放下物品i,则就会考虑前i-1个物品能否将容量为j的背包放满,dp[i][j]=dp[i-1][j-1]
    边界条件:j=0,容量为0的背包是一定能放满的,dp[i][0]=true
    dp[i][nums[i]]=true,物体i一定能将容量为nums[i]的背包

特殊情况:sum/2为奇数是不能分割成相等的两部分;nums的长度小于2也不能 ;如果nums中最大的数大于sum/2则,剩下的数和一定比最大数小,也不能划分成相等的两部分

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    let n = nums.length;
    if(n<2){//nums的长度小于2也不能分割成相等的两部分
        return false;
    }
    let sum = nums.reduce((preV, curV) => preV+ curV);
    let target = sum/2;
    if(sum%2 !== 0){//sum为奇数是不能分割成相等的两部分;
        return false;
    }
    let maxV = Math.max(...nums);
    if(maxV > target){
        return false;
    }
    //dp[i][j]表示前i个物品是否能装满容积为j的背包,当dp[i][j]为true时表示恰好可以装满
    let dp = new Array(n).fill(false).map(item=> new Array(target+1).fill(false));
    for(let i=0;i<n;i++){
        dp[i][0] = true;
    }
    dp[0][nums[0]] = true;
    for(let i=1;i<n;i++){
        for(let j=0;j<=target;j++){
            if(j>=nums[i]){
             //dp[i - 1][j]表示不装入第i个物品
               //dp[i - 1][j-num]表示装入第i个,此时需要向前看前i - 1是否能装满j-num
                dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
            }else{//j放不下物品i,只能看j能不能放下前i-1个物品
                dp[i][j] = dp[i-1][j];
            }
        }
    }

    return dp[n-1][target];
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值