416. 分割等和子集
难度:中等
就是很经典的0/1背包问题,背包容量有限,对于每个物品要么全选要么不选,而且每个位置只能选一次,看最后能不能装满背包
题目描述
解题思路
1、二维dp
dp[i][j]表示前i个物品能不能组合成等于j的
先计算target,就是整个数组和的一半,然后开始装背包
初始化第一列,就是不管多少个物品都能组成容量为0的背包
状态转移方程:dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
对于每个位置要么选要么不选,不选就是dp[i-1][j],选就是dp[i-1][j-nums[i-1]]
这两种情况任何一种是true,那这个位置就是true
/*
* 416. 分割等和子集
* 2020/9/8
* medium
*/
public boolean canPartition(int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
//如果数组总和是奇数,肯定不能分成两个
if(sum%2 != 0) {
return false;
}
int target = sum/2,n = nums.length;
//转换成0/1背包问题,能不能从这个n个数字里找到总和刚好等于target的组合
//dp[i][j]表示前i个物品能不能组合成等于j的
boolean[][] dp = new boolean[n+1][target+1];
for (int i = 0; i <= n; i++) {
dp[i][0] = true;
}
for (int i = 1; i <= n; i++) {
for (int j = nums[i-1]; j <= target; j++) {
dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
}
}
return dp[n][target];
}
一维dp
状态转移方程:dp[j] = dp[j] || dp[j-nums[i]];
要么等于本身,不选,要么选,与上一个状态相同
public boolean canPartition1(int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
//如果数组总和是奇数,肯定不能分成两个
if(sum%2 != 0) {
return false;
}
int target = sum/2,n = nums.length;
//转换成0/1背包问题,能不能从这个n个数字里找到总和刚好等于target的组合
//dp[i][j]表示前i个物品能不能组合成等于j的
boolean[] dp = new boolean[target+1];
dp[0] = true;
for (int i = 0; i < n; i++) {
for (int j = target; j >= nums[i]; j--) {
dp[j] = dp[j] || dp[j-nums[i]];
}
}
return dp[target];
}