回溯问题

  • 组合问题:N个数里面按一定规则找出k个数的集合

  • 切割问题:一个字符串按一定规则有几种切割方式

  • 子集问题:一个N个数的集合里有多少符合条件的子集

  • 排列问题:N个数按一定规则全排列,有几种排列方式

  • 棋盘问题:N皇后,解数独等等

关于回溯。

一个问题两个字符串各选一个组成一个 2 个字符的字符串。最容易想到的是两个循环嵌套。

但是如果说要生成一个长为3、长为 4,或者长度是个参数无法提前知道,那么没法用循环嵌套来表达了。

  • 原问题:构造长为 n 的字符串。

  • 枚举一个字母

  • 子问题:构造长为 n - 1 的字符串。

这个子问题和原问题是很相似的。

这种可以分解的问题,可以使用递归来实现。回溯有一个增量构造答案的过程。

把边界条件和非边界条件的逻辑写对了,那递归代码一定能得到正确的结果。

回溯三问:

  • 当前操作是什么?

  • 子问题是什么?

  • 下一个子问题?

对于电话号码组合,用 path[i] 来记录路径上的字母

  • 枚举 path[i] 处要填入的字母

  • 构造字符串 >= i 的部分

  • 构造字符串 >= i + 1 的部分

dfs(i) -> dfs(i+1)

子集型回溯

模板一

子集型回溯,看每个元素或者不选

比如计算 $ [1, 2] $ 所有的子集,每个元素都可以选或者不选,总共就有 4 种情况。

回溯三问,站在输入的角度思考

  • 当前操作?每个第 $ i $ 个数选或不选

  • 子问题?从下标 $ \ge i $ 的数字中构造子集

  • 下一个子问题?从下标 $ \ge i + 1 $ 的数字中构造子集

时间复杂度,每次递归,只有选或不选两种情况,$ O(2^n) $ 复制的时间是 O(n) 所以,总的复杂度是 $ O(n*2^n) $

模板二

另一个思路,站在答案的角度去考虑,枚举第一个选谁,第二个选谁,每个节点都是答案

  • 当前操作?枚举一个下标 $ \ge i $ 的数字,加入 path

  • 子问题?从下标 $ \ge i $ 的数字中构造子集

  • 下一个子问题?从下标 $ \ge i + 1 $ 的数字中构造子集

应用:分割回文串

组合型回溯

组合问题,在子集问题的基础上,增加一个判断逻辑,比如只要选两个数。

相比子集问题,组合问题是可以做一些额外的优化的。

设 path 长度为 m,总共要选 k 个数,那么还需要选 d = k - m 个数。剩下的可以选的数的个数小于 d,那么就不需要递归了,肯定选不出来。这是一个剪枝技巧。

排列型回溯

全排列,全排列的长度就是数组长度,全排列的个数就是元素个数的阶乘。

N 皇后

最后更新于

这有帮助吗?