2021.9.6 buckle - split and subset

The first question of 0-1 knapsack application is also the first question at the beginning of school. To tell you the truth, it would be difficult for me to think about this topic if I hadn't already reminded that it is the application of 0-1 backpack, and I still couldn't think of it in the end.

Title Description:

Here you are   Contains only positive integers   of   Non empty   array   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.

Method 1:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if (n == 1)     //There is only one element, which must not be separated
        {
            return false;
        }
        int sum = 0;
        int maxnum = INT_MIN;
        for (int i = 0; i < n; i++)
        {
            sum += nums[i];
            maxnum = max(maxnum, nums[i]);
        }
        if (sum % 2 != 0 || maxnum > sum / 2)   //If the sum of elements in the array is odd, it must not be split; If there is an element greater than half of the sum of elements, it must not be split
        {
            return false;
        }
        vector<vector<int>> f(n , vector<int>(sum / 2 + 1 , false));
        //f[i][j] indicates whether the sum of some elements is equal to j for elements with subscript 0 - i. If yes, it is true; otherwise, it is false

        f[0][nums[0]] = true; //Initializes an element with a subscript of 0 whose value is equal to nums[0]

        for (int i = 1; i < n; i++)
        {
            for (int j = 1; j <= sum / 2; j++)
            {
                if (j < nums[i])
                {
                    f[i][j] = f[i - 1][j];
                    //If J < nums[i], whether the sum of some elements (certainly no nums[i]) is equal to j is equivalent to considering only the elements with subscript 0 - i-1
                }
                else if (j == nums[i])
                {
                    f[i][j] = true; 
                    //In special cases, f[i][j] is also true when num [i] is exactly equal to j
                }
                else if (j > nums[i])
                {
                    f[i][j] = f[i - 1][j] || f[i - 1][j - nums[i]];
                    //The former means not taking num [i], and the latter means taking num [i]
                }
            }
        }
        return f[n - 1][sum / 2];
    }
};

Refer to these two questions: https://mp.weixin.qq.com/s?__biz=MzUxNjY5NTYxNA==&mid=2247486632&idx=1&sn=4bb229e3f3f5774ac684c0127c88c984&chksm=f9a23ff9ced5b6effc0f579b60dd1e4910b362c6417f19cd2086e77472da525dfa8475afd4ab&cur_album_id=1485825793120387074&scene=189#rd , and https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/

About why you can use 0-1 backpack:

  Because the solution of the first problem is not easy to understand, I use the solution of the second problem.

About recurrence relation:

Let f[i][j] indicate whether the sum of some elements is equal to j for elements with subscript 0 - i. If yes, it is true; otherwise, it is false. Then, for nums[i], there are two cases: if nums[i] is taken, f[i][j] = f[i-1][j]; If nums[i] is not taken, then f[i][j] = f[i-1][j-nums[i]]. That is, f[i][j] = f[i-1][j] | f [I-1] [j-nums[i]].

However, the size relationship between J and nums[i] must also be considered: ① if J < nums[i], it means that there can be no element sum equal to j when considering nums[i], so f[i][j] = f[i-1][j]; ② If j==nums[i], it means that a single element is exactly equal to j, which is also true. This is a special case and needs to be considered separately; ③ If J > nums[i], then the above f[i][j] = f[i-1][j] 𞓜 f [I-1] [j-nums[i]].

So there are the following codes:

                if (j < nums[i])
                {
                    f[i][j] = f[i - 1][j];
                    //If J < nums[i], whether the sum of some elements (certainly no nums[i]) is equal to j is equivalent to considering only the elements with subscript 0 - i-1
                }
                else if (j == nums[i])
                {
                    f[i][j] = true; 
                    //In special cases, f[i][j] is also true when num [i] is exactly equal to j
                }
                else if (j > nums[i])
                {
                    f[i][j] = f[i - 1][j] || f[i - 1][j - nums[i]];
                    //The former means not taking num [i], and the latter means taking num [i]
                }

About initialization:

First initialize all elements in F as false, and then for f[i][0]: f[i][0] is false because the sum of no elements is equal to 0; For f[0][nums[0]]: f[0][nums[0]] is true because the value of the element with subscript 0 is nums[0].

Spatial optimization of method 1:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if (n == 1)     //There is only one element, which must not be separated
        {
            return false;
        }
        int sum = 0;
        int maxnum = INT_MIN;
        for (int i = 0; i < n; i++)
        {
            sum += nums[i];
            maxnum = max(maxnum, nums[i]);
        }
        if (sum % 2 != 0 || maxnum > sum / 2)   //If the sum of elements in the array is odd, it must not be split; If there is an element greater than half of the sum of elements, it must not be split
        {
            return false;
        }
        vector<int> f(sum / 2 + 1, false);
        //f[j] indicates whether the sum of some elements is equal to j for elements with subscript 0 - i. If yes, it is true; otherwise, it is false

        f[nums[0]] = true; //Initializes an element with a subscript of 0 whose value is equal to nums[0]

        for (int i = 1; i < n; i++)
        {
            for (int j = sum / 2; j >= nums[i]; j--)
            {
                if (j == nums[i])
                {
                    f[j] = true;
                    //In special cases, f[i][j] is also true when num [i] is exactly equal to j
                }
                else if (j > nums[i])
                {
                    f[j] = f[j] || f[j - nums[i]];
                    //The former means not taking num [i], and the latter means taking num [i]
                }
            }
        }
        return f[sum / 2];
    }
};

Like the optimization of 01 knapsack problem, changing two-dimensional to one-dimensional, it should also be noted that when traversing, the knapsack should be traversed from large to small. The reason is that when using two-dimensional vector, the value of f[i][j] during traversal depends on the value of [i-1] layer, and the value of [i-1] layer will not change; However, when using a one-dimensional vector, if the knapsack is traversed from small to large, the value of F [j-num [i]] may have been overwritten, resulting in an error, so it should be traversed from large to small instead.

Tags: leetcode

Posted on Mon, 06 Sep 2021 23:04:30 -0400 by helpmeplease1234