liuzhch1

LeetCode 416.分割等和子集 | 类0-1背包问题 | 解题思路及代码

Partition Equal Subset Sum

Problem Description

Given a nonempty array nums, which only contains positive number. Find if the array can be divided into two parts such that the sum of each part is equal.

example

Input: nums = [1,5,11,5]
Output: true
Explanation: divide into [1,5,5] and [11].

Ideas

To satisfy two part sum are equal, then each part sum is half of total sum. If the total sum is an odd or the max number in the array is bigger than half of total sum, then it's can't satisfy the requirement.
This problem is similar to 0-1 package problem. What differs is that the total sum we chose can't exceed the capacity of the package in 0-1 package problem, while the chosen number's sum must equal to half of total sum in this question. Similar to traditional 0-1 package problem, we can use dynamic programming to solve this problem.

We use a two-dimensional array boolean dp[n][half+1] for dynamic programming. Where n is the length of nums and half is the half of total sum. We define dp[i][j]:

  • i ranges from 0 to n-1, which means the subset(0, i) of nums.
  • j ranges from 0 to half, which means if the subset(0, i) can constitute a sum of value j.

dp[i][j] is true if subset(0, i) of nums can constitute a sum of value j. Then dp[n-1][half] means in the whole array nums, if it can constitute a sum of half.
The state transition equation is as follows:

\[dp[i][j]=\begin{cases} dp[i-1][j]\ ||\ dp[i-1][j-num[i]]&num[i]\leq j\\ dp[i-1][j]&num[i]>j \end{cases}\]

dp[i][j] means we are deciding if we count nums[i] to constitute a sum of j for subset(0, i).
The upper case means: nums[i]<=j, if we not choose this number, then our result(can constitute or not) is equal to "if the subset(0, i-1) can constitute a sum of j", that isdp[i-1][j]. If we choose this number, then our result is equal to "if the subset(0, i-1) can constitute a sum of j-num[i]". Either of them is true, then we can constitute a sum of j by subset(0, i) of nums.
The lower case means: nums[i]>j, the number is bigger than the number(j) we want to constitute, so we can't choose. Our result is same as dp[i-1][j].

Optimization of space

Observe the transition equation, we found that each line of our two-dimensional array dp only related to the previous line(dp[i] is decided by dp[i-1] only). So we can store the dp in only one line:
When we handle the new line i, the 1-dimensional array stores the i-1 line data! For each line, we ranges j from half to 0. The new state transition equation is as follows:

\[dp[j]=dp[j]\ ||\ dp[j-nums[i]] \]

Since j ranges from half to 0, dp[j] or dp[j-nums[i] store the date of last line(i-1). After each round of j, we updated a line.

Algorithm

If nums.length<2 or sum(nums) is odd or max(nums)>sum(nums)/2, return false.
Create a boolean array dp[sum(nums)/2+1]. For i in range from 0 to num.length, for j in range from sum(nums)/2 to 0, if dp[j]==true or dp[j-nums[i]==true, the dp[j]=true.
Finally, return dp[sum(nums)/2].

The detailed code is as follows

public class p416 {
    public static boolean canPartition(int[] nums) {
        int len = nums.length;
        if (len < 2) {
            return false;
        }
        int totalSum = 0, half = 0, maxNum = 0;
        for (int num : nums) {
            totalSum += num;
            maxNum = Math.max(maxNum, num);
        }
        if (totalSum % 2 != 0 || totalSum / 2 < maxNum) {
            return false;
        }
        half = totalSum / 2;
        boolean[] dp = new boolean[half + 1];
        dp[0] = true;
        for (int i = 0; i < len; i++) {
            int num = nums[i];
            for (int j = half; j >= num; j--) {
                dp[j] = dp[j] | dp[j - num];
            }
        }
        return dp[half];
    }
}

Time&Space Complexity

Time complexity: The outer loop nums.length, the inner loop sum(nums)/2, so the total time complexity is O(nums.length × sum(nums)/2)
Space complexity: After optimization, the space we used are sum(nums)/2, so the total space complexity is O(sum(nums)/2)

posted on 2022-04-26 22:48  liuzhch1  阅读(28)  评论(0编辑  收藏  举报

导航