回溯法从根节点出发,按照深度优先遍历解空间树,搜索满足约束条件的解。在搜索至树中任一节点时,先判断该节点对应的部分解是否满足约束条件,是否超出目标函数的界,也就是判断该节点是否包含问题的(最优)解,如果肯定不包含,则跳过对以该节点为根的子树的搜索,即所谓剪枝;否则,进入以该节点为根的子树,继续按照深度优先搜索。
回溯法的基本行为是搜索,搜索过程使用剪枝函数来为了避免无效的搜索。
剪枝函数包括两类:
- 使用约束函数,剪去不满足约束条件的路径;
- 使用目标函数,剪去不能得到最优解的路径。
问题的关键在于如何定义问题的解空间,转化成树(即解空间树)。解空间树分为两种:子集树和排列树。两种在算法结构和思路上大体相同。
回溯法应用
当问题是要求满足某种性质(约束条件)的所有解或最优解时,往往使用回溯法。
回溯法的实现方法有两种:递归和递推(也称迭代)。一般来说,一个问题两种方法都可以实现,只是在算法效率和设计复杂度上有区别。
子集树
所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间成为子集树。
排列树
所给的问题是确定n个元素满足某种性质的排列时,相应的解空间就是排列树。
模板
dfs(){
// 第一步,检查下标是否满足条件
// 第二步:检查是否被访问过,或者是否满足当前匹配条件
// 第三步:检查是否满足返回结果条件
// 第四步:都没有返回,说明应该进行下一步递归
// 标记
dfs(下一次)
// 回溯
}
main() {
for (对所有可能情况) {
dfs()
}
}