LeetCode-416. Split equal sum subset

Posted by mattcass on Tue, 04 Jan 2022 08:12:18 +0100

Topic source

416. Segmentation and subsets

Title details

Give you a non empty array containing only positive integers, {nums. Please judge whether this array can be divided into two subsets so that the sum of the elements in the two subsets is equal.

Example 1:

Input: num = [1,5,11,5]
Output: true
Explanation: an array can be divided into [1, 5, 5] and [11].

Example 2:

Input: num = [1,2,3,5]
Output: false
Explanation: an array cannot be divided into two elements and equal subsets.

Tips:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

Problem solving analysis

Solution 1: dynamic programming

  1. The most intuitive way to solve this problem is to sum the whole array first. If the array cannot be divided, it must not be divided into two subsets;
  2. In addition, if the sum of array elements can be divided, but the value of some elements in the array is greater than half of the sum of elements, it must not be divided into two subsets.
  3. When we get the sum of elements and divide it by 2 to get ave, this question can actually be converted to: can we find some elements in the array to make it just form ave.
  4. After carefully considering the above analysis, careful students may find that this is somewhat similar to the 0-1 knapsack problem, because the original problem of the 0-1 knapsack problem is to select some elements with the greatest value from the previous i items, but the weight does not exceed the weight of the knapsack. This question can also be converted to a 0-1 knapsack problem, because these elements can only be selected once at most.
  5. We set a state transition equation: \ (Boolean dp[i][j] = DP [i-1] [J] | DP [i-1] [j-num [i]] \). It should be noted that our \ (dp[i][j] \) is expressed as: whether some of the first I elements can form elements and exactly J. In addition, the transfer equation here is expressed as whether the first i-1 elements can form and be j without selecting the current element, or whether they can form and be j if the current element is selected.
  6. It should be noted that in the process of traversal, when J < num [i], this state also needs to be transferred according to the previous state: that is \ (dp[i][j] = dp[i-1][j] \).
  7. Finally, it is about the setting of boundary value. When j is 0, we can see the possibility of selecting some elements from the previous I elements and 0. Of course, this is possible, because we can select 0 elements, so we set \ (dp[i][0] \) to true. Some people may ask, why not set it to true when I is 0? This is because 0 elements cannot be constructed into any elements and, so it is set to false.
class Solution {
    public boolean canPartition(int[] nums) {
        // Sum - "" average - "" if it cannot be divided, it must not be divided - "" if the value of an element in the array is greater than the average, it cannot be divided
        int n = nums.length;
        int sum = 0;
        int max = 0;
        for(int i=0; i<n; i++){
            sum += nums[i];
            max = Math.max(max, nums[i]);
        }
        if((sum & 1) == 1){// If it is an odd number, return false directly
            return false;
        }
        int ave = sum >> 1;
        if(max > ave){// If the maximum value in the array is greater than the average value, it also directly returns false
            return false;
        }
        boolean[][] dp = new boolean[n+1][ave + 1];// dp[i][j] indicates whether the first I numbers can form j
        // dp[i][j] = dp[i-1][j] | dp[i-1][j-nums[i]]

        for(int i=0; i<n; i++){
            dp[i][0] = true;
        }
        for(int i=1; i<=n; i++){
            for(int j = 0; j<=ave; j++){
                if(j >= nums[i-1]){
                   dp[i][j] = dp[i-1][j] | dp[i-1][j-nums[i-1]];
                }else{
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n-1][ave];
    }
}

Solution 2: dynamic programming compressed array

  1. As mentioned earlier, this is a topic of dynamic programming. Once it comes to dynamic programming, we should always keep in mind that can we use compressed arrays to compress space?
  2. For this problem, we can use compressed array to compress, because the current state only depends on the previous state. It should be noted that, like the traditional 0-1 knapsack, the reverse order needs to be used when traversing the second dimension, because only when traversing backwards, \ (dp [i-num [i]] \) can represent the dp state of the previous round.
  3. In addition, for the setting of initial state or boundary value, you only need to set dp[0] to true.
class Solution {
    public boolean canPartition(int[] nums) {
        // Sum - "" average - "" if it cannot be divided, it must not be divided - "" if the value of an element in the array is greater than the average, it cannot be divided
        int n = nums.length;
        int sum = 0;
        int max = 0;
        for(int i=0; i<n; i++){
            sum += nums[i];
            max = Math.max(max, nums[i]);
        }
        if((sum & 1) == 1){// If it is an odd number, return false directly
            return false;
        }
        int ave = sum >> 1;
        if(max > ave){// If the maximum value in the array is greater than the average value, it also directly returns false
            return false;
        }
        boolean[] dp = new boolean[ave + 1];// dp[i][j] indicates whether the first I numbers can form j
        // dp[i][j] = dp[i-1][j] | dp[i-1][j-nums[i]]
        dp[0] = true;

        for(int i=1; i<=n; i++){
            for(int j = ave; j >= nums[i-1]; j--){
                dp[j] = dp[j] | dp[j-nums[i-1]];
            }
        }
        return dp[ave];
    }
}

Operation results

Topics: leetcode Interview Dynamic Programming dp