タイトル説明
https://leetcode-cn.com/problems/partition-equal-subset-sum/
解決策(ナップサックタイプのdp問題)
公式の問題解決の章は次のとおりです。
正直に問題を実行しましょう。
これはバックパックタイプのdp問題です。
- 現在の問題を考えると、アイテムの数、バックパックの重量、いっぱいになるかどうかなど、説明する変数は何ですか。それが最初のステップで行う必要があります。2つのポイントを明確にすること。 「status」と「choice」は異なります。サブシーケンスと文字列の問題ですが、サブ問題の現象を説明するために、dp [i] [j]などの対応する添え字はs [i… j]、ただしi個のアイテムバックパックを容量jで満たすか、最大値に達することができる、中に何を詰めるかを選択します。
- したがって、2番目のステップで行う必要があるのは、dp配列の定義を明確にすることです。
- 最後に、3番目のステップは、「選択」に基づいて状態遷移のロジックについて考えることです。
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,是否能恰好装满
}
}