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

(Java) LeetCode 416. Partition Equal Subset Sum —— 分割等和子集

工作 href ... 發現 地方 而是 subset emp solution

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: [1, 2, 3, 5]

Output: false

Explanation: The array cannot be partitioned into equal sum subsets.

解法一:

一上來首先想到的並不是動態規劃,而是廣度優先搜索。首先觀察一下題目,數組能被對半分的前提是數組元素和必須是偶數。那麽本題就轉化成了之前做過的一個問題,即LeetCode 40. Combination Sum II —— 組合總和 II,尋找target為數組元素總和一半的元素組合。同樣每個元素只能用一次,還有重復元素。只是40題是找路徑的結果集合,而本題只需要找其中任何一個滿足條件的組合即可。代碼照搬,稍加修改即可。

提交之後速度擊敗96.3%,證明這樣做是可以的。可是看到有些排名靠前的答案一樣用了DFS的思想但卻沒有排序,其中搜索下一個元素的地方改成了從數列尾部向前搜索。經過測試我發現這道題的test case應該都是排序過的。如果把用DFS思想還沒有排序的代碼復制過來,之後測試這個例子{100, 1, 1, ...(共98個1)}的話,是會提交超時的。可見LeetCode的test case不完整,且有些人鉆了空子。分析一下為什麽會超時,因為如果不排序,去重的工作量就會很大。如果不去重,就會一遍又一遍驗證結果相同的集合,這樣就會超時。所以這道題一定要去重,去重就一定要排序才不會超時。

解法二:

這道題還可以用動態規劃,即將每次遍歷到的數的放入和不放入結果集合的狀態都存起來。有點像背包問題,每次放或者不放一個數進去都會影響以後是否能放入未來的數。


解法一(Java)

class Solution { 
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int num : nums)
            sum += num;
        if ((sum & 1) == 1) return false;
        sum >>= 1;
        Arrays.sort(nums); //排序以便去重
        return dfs(nums, 0, sum);
    }
    
    private boolean dfs(int[] nums, int i, int target) {
        if (target == 0) return true;
        if (i > nums.length || target < 0) return false;
        boolean mark = false; //相當於第40題的list,即把路徑存儲在這裏
        for (int p = i; p < nums.length; p++) {
            int newSum = target - nums[p]; //添加到路徑
            mark = mark || dfs(nums, p+1, newSum); //mark本身相當於回溯,並和搜索下一個節點的結果相結合
            while (p < nums.length - 1 && nums[p] == nums[p+1]) p++; //去重,利用排序特點
        }
        return mark;
    }
}

解法二(Java)

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int num : nums)
            sum += num;
        if ((sum & 1) == 1) return false;
        sum >>= 1;
        boolean[] dp = new boolean[sum + 1];
        dp[0] = true;
        for (int j = 0; j < nums.length; j++) {
            for (int i = sum; i >=nums[j]; i--)
                dp[i] = dp[i] || dp[i-nums[j]];
            if (dp[sum]) return true;
        }
        return dp[sum];
    }
}

(Java) LeetCode 416. Partition Equal Subset Sum —— 分割等和子集