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.