回溯法:以深度优先方式系统搜索问题的解
在问题的解空间树中,按深度优先策略,从根节点出发搜索解空间树
- 当搜索到解空间树的任一结点时,判断该结点是否包含问题的解
- 如果确定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯;(剪枝)
- 否则,进入该子树,继续深度优先搜索;
求解问题的性质
- 求问题的所有解时,要回溯到根,且根节点的所有子树都被搜索后才结束
- 求问题的一个解时,只要搜索到问题的一个解即可
回溯法的算法框架
问题的解空间
- 定义:问题的解空间至少应包含问题的一个最优解
- 组织:通常组织为树或图的形式——有利于回溯法对整个解空间的搜索
回溯法的基本思想
- 确定解空间的组织结构
- 从开始结点(根结点)出发,深度优先搜索整个解空间。(这个开始结点成为活结点,也为当前的扩展结点)
- 在当前扩展结点处,向纵深方向搜索一个新结点。(这个新结点成为活结点,也为当前的扩展结点)
- 如果不能向纵深方向搜索了,则当前结点成为死结点。并且往回移动(回溯)到最近的活结点处
- 回溯法按上述方式递归地在解空间中搜索,直到找到所要求的解或解空间中无活结点为止
如何避免回溯法的无效搜索
剪枝函数:
- 用 约束函数 在扩展结点处剪去不满足约束的子树——0-1背包问题
- 用 上界函数 剪去得不到最优解的子树——TSP问题
递归回溯
void backtrack(int t) // 递归深度t
{
if(t > n) // 最大深度n
output(x);
else
for(int i = f(n, t); i <= g(n, t); ++i)
{ // 在当前扩展结点处未搜索过的子树
x[t] = h(i);
if(constrain(t) && bound(t)) // 约束函数 和 上界函数
backtrack(t+1);
}
}
迭代回溯
void iterativeBacktrack()
{ // 采用树的非递归深度优先遍历(树的遍历)
stack<T> s; // 存结点的堆栈
T p = t; // 当前扩展结点p,根结点t
while(!s.empty())
{
for(int i = 0; i < p.child.size(); ++i)
{ // 在当前扩展结点处未搜索过的子树
d = p.child[i]; // 子树结点d
if(constrain(d) && bound(d)) // 约束函数 和 上界函数
s.push(d);
}
p = s.top();
s.pop();
}
}
子集树和排列树
-
子集树
当所给问题是从n个元素的集合S中找出S满足某种性质的子集时(选或不选某个结点),相应的解空间树被称为子集树。
如0-1背包问题,这类问题通常有2n个叶结点,其结点总数为2n+1-1。
遍历子集树需要Ω(2n)计算时间
-
排列树
当所给问题是确定n个元素满足某种性质的排列时,相应的解空间树被称为排列树。
如旅行商问题TSP,这类问题通常有n! 个叶结点
遍历排列树需要Ω(n!)计算时间

实例分析
旅行商问题 (排列树)
- 问题描述:
某销售商要到若干个城市去推销商品,已知各城市之间的路程(或费用)。要求为给旅行商选择一条从驻地出发的路径,经过每个城市一次,最后返回驻地,使得该路径(或总的旅费)最短(或最小)。
- 问题分析:
为NP难问题。设𝐺=(𝑉,𝐸)是一个带权图。图中各边的权为正数。图的一条周游路线是包括𝑉中的每个顶点在内的一条回路。周游路线的费用是这条路线上所有边的权之和。——旅行商问题就是要在图𝐺中找出费用最小的周游路线。
- 实例分析:



对于n = 4的TSP问题,可能周游的路线有6条。
对于n = N的TSP问题,可能周游路线有(N-1)!条
- 回溯法求解过程:


- 剪枝函数的引入:
采用当前已知的最优解(费用X)为标准,若当前路径长度 ≥ X,则表明以此结点为根结点的子树中不包含最优解,将子树中所有结点都设置为死结点,并向当前结点的最近祖先结点回溯。
- 时间复杂度及算法:
O(n!)
常用TSP解决方案:遗传算法;模拟退火;神经网络;并行算法
骑士巡游问题(子集树)
符号三角形问题(子集树)
N皇后问题(子集树)
最大团问题(子集树)
图的m着色问题(子集树)
圆排列问题(排列树)
电路板排列问题(排列树)
连续邮资问题
回溯法效率分析
影响因素
- 产生 状态点x[k] 的时间
- 满足显约束的 状态点x[k] 的个数
- 计算约束函数constrain的时间
- 计算上界函数bound的时间
- 满足约束函数和上界函数的所有结点个数
解空间的结构一旦确定,前3个因素就可以确定,剩下的就是考虑回溯过程中生成的结点个数
重排原理
在搜索试探时,选取x[i]的值顺序是任意的。在其他条件相当的前期下,让可取值最少的x[i]优先有效。这样,在低层剪枝时,可以剪去更多的子树,可提高回溯法效率。
概率方法估计将产生的结点数
原因:
对两个非常相近的实例,其产生的结点数也会存在很大的差别
方案:
在解空间树上产生一条随机路径,然后沿该路径估算解空间中满足约束条件的结点数m
多选取几条不同的路径,分别计算m,然后平均,估算更准确
小结
-
回溯法的基本思想
-
问题解空间的定义和组织
-
剪枝函数的设计
-
掌握利用回溯法求解问题的方法
-
效率分析
PS: 本节内容偏于设计,于《算法导论》中无本章节,用于补充