主要内容 #
- 算法介绍
- 问题回顾
- 适用范围和优劣
1. 算法介绍 #
回溯法是一种类似枚举的搜索尝试过程,既然是枚举,那么就会遍历解空间树中的所有解(或者是“路径”),搜索的过程按照DFS原则,而尝试就意味着,在遍历的过程中,有可能到达某一个结点后,发现不能够满足约束条件,在这次尝试中,这条“路”是不优的,将走不通,即无法找到所求的解,那么就会回退到上一步的状态,重新作出选择。如果即满足约束条件,但是依然没有获得有效的解,那么我们就需要在此基础上做下一步选择,即将当前结点当做一个新的根结点。所以经常会使用递归的方法。如果一步步下来的选择结果正好满足我们所求的问题,那么就是一个有效的解。
回溯法思想:在包含问题所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度搜索解空间树。当搜索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被检索一遍才结束。而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
2. 具体流程 #
(1)设置初始化的方案(给变量赋初值,读入已知数据等);
(2)选择所到深度层(k)中其中的一个节点进行试探,如果k层的节点已经全部都试探完毕,则进入(7);
(3)如果试探不成功(不满足约束条件),则转入(2);
(4)如果试探成功,则进入下一个深度层进行试探。
(5)如果正确解还没找到,则进入(2);
(6)如果找到了正解,如果问题只求其中的一个解,那么可以退出程序了,如果是求一组解,那么将该种解存储(一般用一个类中的变量);
(7)退回上一步的状态(深度为k-1时,变量的状态),如果没有退到头,则进入(2);
(8)已退到头则结束或打印无解。
算法框架:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:选择列表)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
3. 适用范围和优劣 #
回溯法,一般可以解决如下几种问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
回溯法的优点在于其程序结构明确,可读性强,易于理解,而且通过对问题的分析可以大大提高运行效率。但是,对于可以得出明显的递推公式迭代求解的问题,还是不要用回溯法,因为它花费的时间比较长。