一.問題描述
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7]
and target 7
,
A solution set is:
[ [7], [2, 2, 3] ]即給定一個int的list,即一個目標和target,找出list中所有和爲target的int值組合,其中list中的int可以重複使用,也可以不按順序,但是返回值中不能有重複的組合。
二.代碼編寫
看到這種題型我的第一反應是dp,但是dp並不適合這種要返回所有具體組合的題,(dp更適合返回是否存在這樣的組合)。個人認爲,因爲dp是用空間換取時間,但這裏需要存儲的組合數過多。
這種題是典型的DFS的思想,想到DFS就一定要想到回溯,實際上回溯就是DFS加上剪枝的思想(剪枝就是當不滿足條件的時候及時回退,不繼續往下遍歷)。
這個題目要注意的是停止條件的確定,用到棧的思想,先入棧不滿足條件的時候將其彈出。
我們始終
代碼編寫上要注意的是,我們用list_a存儲當前的棧,一旦滿足條件(==target)時要將其append到輸出list中,由於list是可變類型,所以賦值的時候要用到deepcopy()。
代碼如下:
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# backtracking
list_a = []
return_list = []
i = 0
# step1: sort the candidates
candidates.sort()
sum = 0
while i < len(candidates) or list_a!=[]: # stopping condition
while i >= len(candidates): # when this way going to an end
if list_a == []: # when list_a is None and i>=len(Candidates), then stop searching
break
aaa = list_a.pop() # then up backtracking
sum -= aaa
i = candidates.index(aaa)+1
if i>=len(candidates): # stop searching
break
sum += candidates[i]
list_a.append(candidates[i]) # add it to the stack
if sum > target: # backtracking
ind = list_a.pop()
sum -= ind
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
#i += 1
i = candidates.index(ind)+1
#i = candidates.index(list_a[-1])
#i += 1
elif sum == target:
#list_a.append(candidates[i])
return_list.append(copy.deepcopy(list_a)) # dont append list_a directly!!!
ind = list_a.pop()
sum -= ind
#i += 1
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
#list_a.pop()
#i += 1
i = candidates.index(ind)+1
'''
else:
sum += candidates[i]
list_a.append(candidates[i])
'''
'''
sum = 0
list_b = []
while candidates[-1]!=target and sum <= target:
list_b.append(candidates[-1])
sum += candidates[-1]
if sum == target and (return_list==[] or return_list[-1]!=list_b):
return_list.append(copy.deepcopy(list_b))
break
'''
return return_list
基本上就是樹的DFS(Depth First Search),回溯的時候網上回溯兩個節點,最上面形式化了一個頭結點。
三.CombinationSumII
這一題和39的不同之處在於每個元素只能使用一次,當然了,不是說在整個尋找過程中只能用一次,而是說在一個結果中不能重複使用。
實現的注意點:
1)每次循環指針i加1;
2)有重複元素,index的時候要index到最後一個(python對list沒有rindex,所以自定義實現了)
3)一種特殊情況(candidates = [3,1,3,5,1,1], target = 8),存在一種情況就是分支走到最後一個元素,sum仍然小於target,這時候的回溯要直接回溯到head,否則會不停循環,TLE
代碼:
'''
@ author: wttttt at 2016.11.19
@ problem description see: https://leetcode.com/problems/combination-sum-ii/
@ solution explanation see: http://blog.csdn.net/u014265088/article/details/53138838
@ backtracking(pruned dfs)
'''
import copy
class Solution(object):
# the function used to find the last targeted element in the list, somewhat like rindex() for str.
def findlast(self,list_test,tar):
i = 0
while tar in list_test[i:]:
i += list_test[i:].index(tar)
i += 1
return i-1
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# backtracking
list_a = []
return_list = []
i = 0
# step1: sort the candidates
candidates.sort()
sum = 0
while i < len(candidates) or list_a != []: # stopping condition
# another difference contrast to leetcode39 is:
# one conditon that the sumii should notice is that because u can just use each element once,
# so that u may backtracking when sum<target, at this situation, u should pop() all the element in list_a.
while i >= len(candidates) and list_a!=[]: # when this way going to an end
if list_a == []: # when list_a is None and i>=len(Candidates), then stop searching
break
aaa = list_a.pop() # then up backtracking
sum -= aaa
i = self.findlast(candidates,aaa) + 1
if i >= len(candidates): # stop searching
break
sum += candidates[i]
list_a.append(candidates[i]) # add it to the stack
if sum > target: # backtracking
ind = list_a.pop()
sum -= ind
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
i = self.findlast(candidates,ind)
elif sum == target:
# list_a.append(candidates[i])
return_list.append(copy.deepcopy(list_a)) # dont append list_a directly!!!
ind = list_a.pop()
sum -= ind
# i += 1
if len(list_a) != 0:
ind = list_a.pop()
sum -= ind
i = self.findlast(candidates, ind)
# main difference contrast to leetcode39
i += 1
return return_list
So = Solution()
candi = [3,1,3,5,1,1]
target = 8
print So.combinationSum2(candi,target)
#print So.findlast([1,1,2,5,6,7,10],1)