Given a non-empty array that contains only positive integers. Is it possible to split this array into two subsets so that the sum of the elements of the two subsets is equal.
Note: There will be no more than 100 elements in each array
The size of the array will not exceed 200
Example 1: Input: [1, 5, 11, 5].
Output: true
Explanation: Arrays can be split into [1, 5, 5] and [11].
Example 2: Input: [1, 2, 3, 5].
Output: false
Explanation: An array cannot be divided into two elements and equal subsets.
DFS dynamic programming, Ali Tencent's byte abstraction capabilities occupy an absolutely important position in both engineering and algorithms. For example, we can abstract the above question as:
Given a non-empty array, and is sum, can you find such a subsequence that sums to be 2 sum
We have done two numbers, three numbers, four numbers, will it be more comfortable to see this kind of similar questions, and the thinking will be a little more open.
When veteran drivers see the transformed question, they will immediately think of the backpack problem, which will be provided hereDepth-first searchwithBackpacksTwo solutions.
Let's take a look at the description of the problem, sum has two cases, if sum % 2 === 1, then there must be no solution, because sum 2 is a decimal, and the array is composed of integers, and the sum of subarrays cannot be decimals. If sum % 2 === 0, we need to find a subsequence that sums to 2 sum For 2, we need to find subnums in nums that satisfies the condition. This process can be analogous to a large basket with n balls, each ball representing a different number, we use a small basket to grab the ball, so that the sum of the balls is 2 sum. Then a natural idea is that for each ball in the big basket, we think about taking it or not, and if we are patient enough, we will definitely be able to exhaust all the situations and judge whether there is a solution. The above thinking is expressed as pseudo** as follows:
Let target = sum 2, nums be the input array, cur be the index of the current number to be selectednums be the input array, target be the current sum target, cur be the current judgment function dfs(nums, target, cur) if target < 0 or cur > numslength return false Otherwise, if target = 0, the answer is found, return true Otherwise, take the current number or not, enter the recursive dfs(nums, target - nums[cur], cur + 1) |dfs(nums, target, cur + 1)
Because each number is considered to be taken or not, the time complexity here is o(2 n), where n is the length of the nums array, and the j**ascript implementationvar canpartition = function (nums) sum = sum / 2; return dfs(nums, sum, 0);}function dfs(nums, target, cur) return ( target === 0 ||dfs(nums, target - nums[cur], cur + 1) |dfs(nums, target, cur + 1) )
As expected, this is a timeout, let's see if there is room for optimization.
If the maximum value in nums is > 2 sum, then there must be no solution During the search, we take or do not take each number, and all items in the array are positive. We set the sum of the numbers aspickedsum
, it is not difficult to pickedsum <= 2 sum, and the number of discards is requireddiscardsum
, it is not difficult to pickedsum <= 2 sum. We introduce both constraints to enhance pruning:
The optimized ** is as follows.
var canpartition = function (nums) sum = sum / 2; nums = nums.sort((a, b) => b - a); if (sum < nums[0]) return dfs(nums, sum, sum, 0);}function dfs(nums, pickremain, discardremain, cur) if (pickremain < 0 ||discardremain < 0 ||cur > nums.length) return ( dfs(nums, pickremain - nums[cur], discardremain, cur + 1) |dfs(nums, pickremain, discardremain - nums[cur], cur + 1) )
Leetcode is AC, but the time complexity is o(2 n), and the algorithm time complexity is very poor, let's see if there is any better.
When using DFS, we don't care about the rules of fetching, as long as we make sure that the number to be fetched has not been fetched before. What happens if we have a regular arrangement of the number taking strategy, for example, the first number is arranged in the first place, and the second number is arranged in the second place, when judging that the i-th digit is a number, we already know whether the first i-1 number is a combination of all subsequences each time, and record the set s as the sum of this subsequence. Looking at the situation of taking the ith digit, there are two situations to take or not to take.
If target-nums[i] is in the set s, it returns true, indicating that the first i number can be found and the sequence of the target is not taken, if the target is in the set s, it returns true, otherwise it returns false, that is, whether the first i number can constitute a subsequence of the sum of the target depends on the number of the first i-1.
Noting f[i, target] is the possibility of whether the first i numbers in the nums array can form a subsequence of and is target, then the state transition equation is.
f[i, target] = f[i - 1, target] |f[i - 1, target - nums[i]]
The state transition equation is out, ** is very good to write, DFS + dp can be solved, there are unclear can refer to the recursion and dynamic programming, here only provide dp solution pseudo** representation
n = nums.lengthtarget is the sum of nums numbers, if the target is not divisible by 2, return false so that dp is n * target's two-dimensional matrix, and initially false traversal 0:n, dp[i][0] = true indicates that the sum of the first i numbers is 0 to n traversal 0 to target if the current value j is greater than nums[i] dp[i + 1][j] = dp[i][j-nums[i]] dp[ i][j] else dp[i+1][j] = dp[i][j]
The time complexity of the algorithm is o(n*m), the space complexity is o(n*m), and m is sum(nums) 2
j**ascript implementation
var canpartition = function (nums) else const dp = array.from(nums).map(()=> array.from().fill(false) )for (let i = 0; i < nums.length; i++)for (let i = 0; i < dp.length - 1; i++)return dp[nums.length - 1][sum];}
Let's see if there's room for optimization, and look at the state transition equationf[i, target] = f[i - 1, target] |f[i - 1, target - nums[i]]
The state of row n depends only on the state of row n-1, which means that we can compress the two-dimensional space into one dimension.
False**.
Traversal 0 to n Traversal j from target to 0 if the current value j is greater than nums[i] dp[j] = dp[j-nums[i]] dp[j] else dp[j] = dp[j].
Time complexity o(n*m), space complexity o(n) j**ascript implementation.
var canpartition = function (nums) sum = sum / 2; const dp = array.from().fill(false); dp[0] = true; for (let i = 0; i < nums.length; i++)return dp[sum];}
Actually, this problem and leetcode 518 are skinning questions, and they can both be classified as backpack problems, there are n items and a backpack with a capacity of v. The cost of putting the ith item into it is CI and the value you get is WI. Solve which items to fit in your backpack to maximize the sum of values.
The peculiarity of the backpack problem is that we can choose to put or not put each item. Let f[i, v] indicate the state in which the first i items are placed in a backpack with capacity v.
For the backpack described above, f[i, v] denotes the maximum value that can be obtained, then the state transition equation is.
f[i, v] = max
Against 416In the case of dividing the sum subset, the state meaning of f[i, v] indicates the possibility that the first i numbers can form a sum of v, and the state transition equation is.
f[i, v] = f[i-1, v] |f[i-1, v-ci]
Let's go back to leetcode 518, and the original question is as followsGiven coins of different denominations and a total amount. Write a function to calculate the number of combinations of coins that can be combined to make up the total amount. Suppose there is an infinite number of coins of each denomination.Bringing in the idea of a backpack, f[i,v] denotes the number of combinations that can be exchanged for v with the first i coins, and the state transition equation is
f[i, v] = f[i-1, v] +f[i-1, v-ci]
j**ascript implementation
/** param amount * param coins * return */var change = function (amount, coins) )fill(0); dp[0] = 1; for (let i = 0; i < coins.length; i++)return dp[amount];}
Note that the inner loop and the outer loop cannot be reversed, that is, the outer layer must traverse coins and the inner layer traverse amount, otherwise coins may be used multiple times and cause inconsistencies
Complexity analysis
Time complexity: $o(amount * len(coins))$Space complexity: $o(amount)$