【leetcode hot 100 416】分割等和子集
解法一:(动态规划)①定义:dp[i]表示是否可以在nums找到元素之和为i,dp[sum/2+1] ②初始状态:dp[0]=true;dp[i]=false ③状态转移方程:dp[i] = dp[i] || dp[i - num];
class Solution {
public boolean canPartition(int[] nums) {
// 定义:dp[i]表示是否可以在nums找到元素之和为i,dp[sum/2+1]
// 初始状态:dp[0]=true;dp[i]=false
// 状态转移方程:dp[i] = dp[i] || dp[i - num];
int n = nums.length;
// 先计算总和
int sum = 0;
for (int num : nums) {
sum += num;
}
// 计算每个子集的和应该为多少
int target = sum / 2;
// 预先判断
// 如果和不能被2整除,则不能分为两个子集
if (sum % 2 != 0) {
return false;
}
// 由于nums只包含正整数,若任意一个数大于target则不能满足
for (int num : nums) {
if (num > target){
return false;
}
}
// 动态规划
boolean[] dp = new boolean[target+1]; // dp[i]=true表示能在数组nums中找到子集和为i
dp[0] = true;
for (int num : nums) {
// 不需要,后面for循环:
// 当i=num时,dp[num]=dp[num]||dp[num-num]=dp[num]||dp[0]=false||true=true
// 而且会错误
// 没有避免一个数被多次取和没有取!!!!
// dp[num] = true; // 给每个dp[num]赋值,num一定存在
// 循环num,尝试把num加进去,确保每个dp[i]只会用到一个num
// 第二层的循环我们需要从大到小计算,因为如果我们从小到大更新 dp 值,那么在计算 dp[j] 值的时候,dp[j−nums[i]] 已经是被更新过的状态,不再是上一行的 dp 值。
for (int i=target; i >= num; i--) {
dp[i] = dp[i] || dp[i - num];
}
if(dp[target]){
// 已经有子集了,提前结束
return true;
}
}
return dp[target];
}
}
注意:
- 循环num,尝试把num加进去,确保每个dp[i]只会用到一个num
- 第二层的循环我们需要从大到小计算,因为如果我们从小到大更新 dp 值,那么在计算 dp[j] 值的时候,dp[j−nums[i]] 已经是被更新过的状态,不再是上一行的 dp 值。
- num一定存在,不需要dp[num] = true; 给每个dp[num]赋值。后面for循环:当i=num时,dp[num]=dp[num]||dp[num-num]=dp[num]||dp[0]=false||true=true;而且会错误,没有避免一个数被多次取和没有取!!!!错误: