Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
Example 1:
Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.
The first idea is to use each number any number of times and collect every sequence that sums to \(target\). With \(n \le 30\) and \(target \le 40\), an unpruned search wastes paths that already exceed the target, and different orders of the same multiset would be counted twice.
A combination ignores order, so each multiset should appear once. Sort first, and only take candidates from the current index rightward.
After sorting, if the remainder \(s\) is already smaller than \(candidates[i]\), everything after is larger and the branch dies. \(dfs(i,s)\) enumerates \(j\) from \(i\), and the recursive call stays at \(j\) so the same value may be reused.
We can first sort the array to facilitate pruning.
Next, we design a function \(dfs(i, s)\), which means starting the search from index \(i\) with a remaining target value of \(s\). Here, \(i\) and \(s\) are both non-negative integers, the current search path is \(t\), and the answer is \(ans\).
In the function \(dfs(i, s)\), we first check whether \(s\) is \(0\). If it is, we add the current search path \(t\) to the answer \(ans\), and then return. If \(s \lt candidates[i]\), it means that the elements of the current index and the following indices are all greater than the remaining target value \(s\), and the path is invalid, so we return directly. Otherwise, we start the search from index \(i\), and the search index range is \(j \in [i, n)\), where \(n\) is the length of the array \(candidates\). During the search, we add the element of the current index to the search path \(t\), recursively call the function \(dfs(j, s - candidates[j])\), and after the recursion ends, we remove the element of the current index from the search path \(t\).
In the main function, we just need to call the function \(dfs(0, target)\) to get the answer.
The time complexity is \(O(2^n \times n)\), and the space complexity is \(O(n)\). Here, \(n\) is the length of the array \(candidates\). Due to pruning, the actual time complexity is much less than \(O(2^n \times n)\).
Method 1 already prunes correctly by looping over “who is next”. The other writing is choose-or-skip at the current index: skip goes to \(i+1\); take stays at \(i\) so the same value may be reused. The search tree looks different; the answers do not. It is the same algorithm unfolded as a binary tree.
We can also change the implementation logic of the function \(dfs(i, s)\) to another form. In the function \(dfs(i, s)\), we first check whether \(s\) is \(0\). If it is, we add the current search path \(t\) to the answer \(ans\), and then return. If \(i \geq n\) or \(s \lt candidates[i]\), the path is invalid, so we return directly. Otherwise, we consider two situations, one is not selecting the element of the current index, that is, recursively calling the function \(dfs(i + 1, s)\), and the other is selecting the element of the current index, that is, recursively calling the function \(dfs(i, s - candidates[i])\).
The time complexity is \(O(2^n \times n)\), and the space complexity is \(O(n)\). Here, \(n\) is the length of the array \(candidates\). Due to pruning, the actual time complexity is much less than \(O(2^n \times n)\).