题目:给定一个非空的正整数数组 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];
};