给你一个 只包含正整数 的 非空 数组 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;
};
经过本题之后,确实感觉对动态规划更有感觉了