极致简单--回溯算法 + 剪枝优化

594 阅读2分钟

回溯函数的组成 

1.回溯出口,当找到了一个问题的解时,存储该解。
2.回溯主体,就是遍历当前的状态的所有子节点,并判断下一个状态是否是满足问题条件的,如果满足问题条件,那么进入下一个状态。
3.状态返回,如果当前状态不满足条件,那么返回到前一个状态。

摘自:blog.csdn.net/weixin_4320…

1 问题描述

给定一个不包含重复数字的序列,返回所有不重复的全排列。

1.1 问题分析

遍历所有的解空间树即可找到答案。
首先定义一个回溯函数

# combination 为当前的状态
backtrack(combination=[])

那么它的出口部分也很好写,就是当combination的长度等于序列的长度时,就找到了问题的一个解。

if len(combination) == len(nums):
       answer.append(combination)

然后是回溯函数的主体部分,我们要遍历当前状态下的所有子节点,并判断子节点是否还符合问题的条件,那么对于这个问题,因为全排列的数是不能够重复的,所以我们的判断方式是当前的数没有包含在combination中,那么进入下一个状态。

for num in nums:
    if num not in combination:
        backtrack(combination+[num])

那么这个问题需要返回上一个状态吗?答案是不需要,因为backtrack的下一个状态的写法是backtrack(combination + [num]),这并不会改变我们当前的combination的值,因为我们没有对combination对象进行一个重新的赋值操作。
如果说修改一下回溯函数的主体。

for num in nums:
    if num not in combination:
    	combination.append(num)
        backtrack(combination+[num])

那么这时候,combination的值被改变了,所以需要写一个返回上一个状态的代码。

for num in nums:
  if num not in combination:
      combination.append(num)
      backtrack(combination)
      combination.pop()

并且,因为我们传入的是相当于是combination对象,所以在存储解的时候需要深拷贝。

if combination.__len__() == nums.__len__():
    solution = copy.deepcopy(combination)
    answer.append(solution)

3.1.2 完整代码

import copy
class Solution:
    def permute(self, nums: list):
        answer = []
        def backtrack(combination=[]):
            if combination.__len__() == nums.__len__():
                solution = copy.deepcopy(combination)
                answer.append(solution)
                return
            for num in nums:
                if num not in combination:
                    combination.append(num)
                    backtrack(combination)
                    combination.pop()
        backtrack()
        return answer

几大习题:

(1)装载问题
(2)0-1背包问题
(3)旅行售货员问题
(4)八皇后问题
(5)迷宫问题
(6)图的m着色问题