1. 程式人生 > >LeetCode 416. 分割等和子集 Partition Equal Subset Sum

LeetCode 416. 分割等和子集 Partition Equal Subset Sum

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));
    }
}