LeetCode 416. 分割等和子集 Partition Equal Subset Sum
阿新 • • 發佈:2018-11-12
9-7 面試中的0-1揹包問題 Partition Equal Subset Sum
題目: LeetCode 416. 分割等和子集
給定一個只包含正整數的非空陣列。是否可以將這個陣列分割成兩個子集,使得兩個子集的元素和相等。
注意:
每個陣列中的元素不會超過 100
陣列的大小不會超過 200
示例 1:
輸入: [1, 5, 11, 5]
輸出: true
解釋: 陣列可以分割成 [1, 5, 5] 和 [11].
示例 2:
輸入: [1, 2, 3, 5]
輸出: false
解釋: 陣列不能分割成兩個元素和相等的子集.
典型的揹包問題,在n個物品中選出一定物品,填滿sum/2的揹包。
其中對“狀態”的定義:F(n, c)考慮將n個物品填滿容量為c的揹包。
根據對狀態的定義,決定狀態的轉移:
F(i, c) = F(i - 1, c) || F(i-1, c - w(i))
import java.util.Arrays;
/// 416. Partition Equal Subset Sum
/// https://leetcode.com/problems/partition-equal-subset-sum/description/
/// 記憶化搜尋
/// 時間複雜度: O(len(nums) * O(sum(nums)))
/// 空間複雜度: O(len(nums) * O(sum(nums)))
public class Solution1 {
// memo[i][c] 表示使用索引為[0...i]的這些元素,是否可以完全填充一個容量為c的揹包
// -1 表示為未計算; 0 表示不可以填充; 1 表示可以填充
private int[][] memo;
public boolean canPartition(int[] nums) {
int sum = 0;
for(int i = 0 ; i < nums.length ; i ++){
if(nums[i] <= 0)
throw new IllegalArgumentException("numbers in nums must be greater than zero.");
sum += nums[i];
}
if(sum % 2 == 1)
return false;
memo = new int[nums.length][sum / 2 + 1];
for(int i = 0 ; i < nums.length ; i ++)
Arrays.fill(memo[i], -1);
return tryPartition(nums, nums.length - 1, sum / 2);
}
// 使用nums[0...index], 是否可以完全填充一個容量為sum的揹包
private boolean tryPartition(int[] nums, int index, int sum){
if(sum == 0)
return true;
if(sum < 0 || index < 0)
return false;
if(memo[index][sum] != -1)
return memo[index][sum] == 1;
memo[index][sum] = (tryPartition(nums, index - 1, sum) ||
tryPartition(nums, index - 1, sum - nums[index])) ? 1 : 0;
return memo[index][sum] == 1;
}
private static void printBool(boolean res){
System.out.println(res ? "True" : "False");
}
public static void main(String[] args) {
int[] nums1 = {1, 5, 11, 5};
printBool((new Solution1()).canPartition(nums1));
int[] nums2 = {1, 2, 3, 5};
printBool((new Solution1()).canPartition(nums2));
}
}
import java.util.Arrays;
/// 416. Partition Equal Subset Sum
/// https://leetcode.com/problems/partition-equal-subset-sum/description/
/// 動態規劃
/// 時間複雜度: O(len(nums) * O(sum(nums)))
/// 空間複雜度: O(len(nums) * O(sum(nums)))
public class Solution2 {
public boolean canPartition(int[] nums) {
int sum = 0;
for(int i = 0 ; i < nums.length ; i ++){
if(nums[i] <= 0)
throw new IllegalArgumentException("numbers in nums must be greater than zero.");
sum += nums[i];
}
if(sum % 2 == 1)
return false;
int n = nums.length;
int C = sum / 2;
boolean[] memo = new boolean[C + 1];
for(int i = 0 ; i <= C ; i ++)
memo[i] = (nums[0] == i);
for(int i = 1 ; i < n ; i ++)
for(int j = C; j >= nums[i] ; j --)
memo[j] = memo[j] || memo[j - nums[i]];
return memo[C];
}
private static void printBool(boolean res){
System.out.println(res ? "True" : "False");
}
public static void main(String[] args) {
int[] nums1 = {1, 5, 11, 5};
printBool((new Solution2()).canPartition(nums1));
int[] nums2 = {1, 2, 3, 5};
printBool((new Solution2()).canPartition(nums2));
}
}