题目描述
https://leetcode-cn.com/problems/partition-equal-subset-sum/
解法(背包类型dp问题)
官方题解开篇就是:
还是老老实实做题吧:
这是背包型dp问题,
- 思考当前的问题里面,要描述的变量有哪些,比如物品数量,背包重量,是否能装满,也就是我们第一步要做的:要明确两点,「状态」和「选择」,它不同于子序列和字符串的问题了,而是描述一个子问题现象,对应的下标如dp[i][j]也不再是从s[i…j]的什么什么,而是i个物品里面选择装什么,能把容量为j的背包装满或达到最大价值。
- 所以我们第二步要做的:要明确dp数组的定义。
- 最后,第三步,根据「选择」,思考状态转移的逻辑。
class Solution {
public boolean canPartition(int[] nums) {
if(nums==null ||nums.length==0||nums.length==1) return false;
int sum = 0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
if(sum%2!=0){//总和不是偶数,则不能划分两个子集元素和
return false;
}
sum = sum/2;///寻找一个子数组的和为sum即可
//定义dp数组----转化为0-1背包问题,在给定的N个物品里面,背包的重量恰好为sum,我们要装物品,恰好能装满背包
boolean [][] dp = new boolean[nums.length+1][sum+1];//dp[i][j]=x表示给定的前i个物品,背包重量为j,是否能够恰好装满的结果
//边界:dp[0][...]的值为false,表示没有物品装
//dp[...][0]值为false,表示要装的重量为0,不装就是true
//-------------------------------默认初始化就是false
for(int i=0;i<nums.length;i++){
dp[i][0] = true;//
}
int n = nums.length;
//递推 已经知道了dp[i][j],----num[i]表示物品i的重量
for (int i = 1; i <= n; i++) {//由于要求dp[i][j],要用到dp[i-1][j]所以两个维度都是从低到高
for (int j = 1; j <= sum; j++) {
if (j - nums[i - 1] < 0) {//第i个物品的重量为nums[i-1]
// 背包容量不足,不能装入第 i 个物品
dp[i][j] = dp[i - 1][j]; //不能装入,则背包重量还是j
} else {
// 装入或不装入背包 装入,则重量为j,上一个的要装的重量也为j
//不装入,则现在dp[i][j] = 去掉要装的nums[i-1]
dp[i][j] = dp[i - 1][j] | dp[i - 1][j-nums[i-1]];
}
}
}
return dp[n][sum];//n个物品,背包容量为sum,是否能恰好装满
}
}
从我们写出的代码里面,我们可以知道递推的值与相邻的几个状态有关,所以我们可以使用状态压缩来缩小空间复杂度。
class Solution {
public boolean canPartition(int[] nums) {
if(nums==null ||nums.length==0||nums.length==1) return false;
int sum = 0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
if(sum%2!=0){//总和不是偶数,则不能划分两个子集元素和
return false;
}
sum = sum/2;///寻找一个子数组的和为sum即可
boolean [] dp = new boolean[sum+1];//dp[i][j]=x表示给定的前i个物品,背包重量为j,是否能够恰好装满的结果
dp[0]=true;
int n = nums.length;
for (int i = 0; i <n; i++) {//由于要求dp[i][j],要用到dp[i-1][j]所以两个维度都是从低到高
for (int j = sum;j>=0 ; j--) {//因为使用了状态压缩,所以之前的dp[j]就是上一次的dp[i-1][j],现在的dp[j]就是dp[i][j];
//j应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果。
if (j - nums[i] >= 0) {//能装的前提下,装或者不装,不能装,则其值还是dp[j] = dp[j],所以这里直接省去
dp[j] = dp[j] | dp[j-nums[i]];
}
}
}
return dp[sum];//n个物品,背包容量为sum,是否能恰好装满
}
}