Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … ,ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5 and target 8,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
1 public static List<List<int>> CominationSumII(int[] candidates, int target) 2 { 3 List<List<int>> ret = new List<List<int>>(); 4 List<int> O = new List<int>(); 5 Array.Sort(candidates); 6 CombinationSumII(candidates, 0, O, target, ret); 7 8 return ret; 9 } 10 11 public static void CombinationSumII(int[] I, int start, List<int> O, int sum, List<List<int>> ret) 12 { 13 if(sum == 0) 14 { 15 List<int> temp = new List<int>(O); 16 ret.Add(temp); 17 return; 18 } 19 20 if(start == I.Length || sum < 0) 21 return; 22 23 int prev = -1; 24 for(int i = start; i < I.Length; i++) 25 { 26 if (I[i] != prev) 27 { 28 O.Add(I[i]); 29 CombinationSumII(I, i + 1, O, sum - I[i], ret); 30 O.RemoveAt(O.Count - 1); 31 prev = I[i]; 32 } 33 } 34 }
代码分析:
这一题跟combination Sum有点不一样了,candidates 中有重复的元素,但是又不能重复使用。想了很久怎么DP,答案是,很难。
最后还是用递归比较方便。
用题目的例子解释一下递归的过程:
candidate set 10,1,2,7,6,1,5 and target 8,
先排列candidate set => 1,1,2,5,6,7,10
{1} => {1,2,5,6,7,10} , target: 8-1=7
{1,1} => {2,5,6,7,10}, target: 7-1=6
{1,1,2} => {5,6,7,10}, target: 6-2=4;
{1,1,2,5} 5 > 4 回溯
{1,1,5} => {6,7,10}, target: 6-5=1;
{1,1,5,6} 6 > 1 回溯
{1,1,6} => {7,10}, target: 6-6=0; {1,1,6} 添加到return list。
{1,1,7} 7 > 6 回溯
{1,2} => {5,6,7,10},target: 7-2=5
{1,2,5} => {6,7,10}, target: 5-5=0; {1,2,5} 添加到return list。
......
要留意一点,因为题目要求不能有重复,所以用了 一个prev来存放递归前的一个数值,如果下一个值跟prev相同,则跳过。比如说,当我们递归回溯到第一步{1}, 然后循环到下一个{1}开始递归,这是prev = 1,所以我们就跳过第二个{1},直接到{2}继续递归。
不明白的话可以试试注释掉有关prev变量的几行,跑跑题目的例子,会发现{1,7}, {1,2,5}会重复出现。
20行已修改,感谢ffengfrank指出错误。