python - leetcode - 39. Combined sum [classic solution - backtracking algorithm]

1. Title

Combination sum
Description:
Given an integer array candidates without repeated elements and a target integer target, find all the different combinations in candidates that can make the sum of the numbers target the target number, and return it in list form. You can return these combinations in any order.

The same number in candidates can be selected repeatedly without limit. Two combinations are different if the chosen number of at least one number is different.

For a given input, the number of different combinations that sum to target is guaranteed to be less than 150.

Example 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

Example 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

Example 3:

输入: candidates = [2], target = 1
输出: []

hint:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • All elements of candidates are different from each other 1 <= target <= 40

2. Analysis of problem-solving ideas

The idea of ​​​​recalling the method:

The important idea of ​​the backtracking method is to traverse all possibilities through enumeration. But the order of enumeration is to go all the way to the dark side. After discovering the dark side, take a step back and then try the road you haven't taken yet. Until all roads have been tried. Therefore, the method of backtracking can be simply understood as: The enumeration method of taking a step back when you can't make it is called the method of backtracking. The retracement point here is also called the retrospection point.

Review key points

Through analysis, it was found that the three key technical points of the backtracking method are:

  • A road leads to darkness
  • take a step back
  • Find another way

Realization of key points

So how can we use code to achieve the above three key points?

  • for loop
  • recursion

Explained below

The function of the for loop is to find another way: you can use the for loop to implement the function of a path selector, which can select all possible branch paths under the current node one by one.
For example: Now you have reached node a, which is like a crossroads. You have reached a from above and can continue to go down. If there are i ways to go down at this time, then you must try all i ways one by one. The function of for is to allow you to try all i downward paths one by one that are neither repeated nor missing.

Recursion can achieve a path to black and a step back:
a path to black: Recursion means continuing to take one step downwards on the path given by for. If we put the recursion inside the for loop, then every time the for loop enters the recursion after giving a path, it will continue to go down. Until the recursive exit (there is no way to go). So this is the way to achieve a road to darkness. After the recursion exits the recursion, it will take a step back.

Therefore, the combination of for loop and recursion can achieve lookback: when the recursion comes out of the recursion exit. The for loop on the previous level will continue to execute. The continued execution of the for loop will give the next feasible path under the current node. Then the recursive call is made, taking another step down along this path that has never been traveled before. This is the epilogue

Having said all that, what is the usual template for backtracking? How do recursion and for work together?

Return code template

def backward():
    
    if (回朔点):# 这条路走到底的条件。也是递归出口
        保存该结果
        return   
    
    else:
        for route in all_route_set :  逐步选择当前节点下的所有可能route
            
            if 剪枝条件:
                剪枝前的操作
                return   #不继续往下走了,退回上层,换个路再走
            
            else#当前路径可能是条可行路径
            
                保存当前数据  #向下走之前要记住已经走过这个节点了。例如push当前节点
        
                self.backward() #递归发生,继续向下走一步了。
                
                回朔清理     # 该节点下的所有路径都走完了,清理堆栈,准备下一个递归。例如弹出当前节点

The pruning operation here refers to: For some problems, as you keep walking, if a certain situation occurs, you will have reached the point where you can no longer go on, and there is no point in going any further. This situation is called a pruning condition.

And DFS is one of the most typical applications of backtracking.

3. Solution

Backtrack

class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # 解法一
        candidates = sorted(candidates)
        ans = []
        def find(s, use, remain):
            for i in range(s, len(candidates)):
                c = candidates[i]
                if c == remain:
                    ans.append(use + [c])
                if c < remain:
                    find(i, use + [c], remain - c)
                if c > remain:
                    return
        find(0, [], target)
        return ans

		# 解法二
        res = []
        def combination(candidates,target,res_list):
            if target < 0:
                return
            if target == 0:
                res.append(res_list)
            for i,c in enumerate(candidates):
                # 为了避免重复 (例如candiactes=[2,3,6,7],target=7,输出[[2,2,3],[3,2,2][7]])
                # 传到的下一个candicate为candicates[i:]
                combination(candidates[i:],target-c,res_list+[c])
        combination(candidates,target,[])
        return res

		# 解法三
        candidates.sort()
        n = len(candidates)
        res = []
        def backtrack(i, tmp_sum, tmp):
            if tmp_sum > target or i == n:
                return
            if tmp_sum == target:
                res.append(tmp)
                return
            for j in range(i, n):
                if tmp_sum + candidates[j] > target:
                    break
                backtrack(j, tmp_sum + candidates[j], tmp + [candidates[j]])
        backtrack(0, 0, [])
        return res

Attached is the dynamic programming solution

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        dp = {
    
    i:[] for i in range(target+1)}

        # 这里一定要将candidates降序排列
        for i in sorted(candidates,reverse=True):
            for j in range(i,target+1):
                if j==i:
                    dp[j] = [[i]]
                else:
                    dp[j].extend([x+[i] for x in dp[j-i]])
        return dp[target]

Guess you like

Origin blog.csdn.net/qq_43030934/article/details/131639344