迷宫问题中的
回溯
主要体现在当没有路可走时,会退回到上一个岔路口,重新在没有走过的路线中找一个没有走过的路走
寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解。
1.当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方法是可行的。
2.若候选解的数量非常大(指数级,大数阶乘),即便采用最快的计算机也只能解决规模很小的问题。
回溯
和分支限界法
是比较常用的对候选解进行系统检查两种方法。
回溯法实际上一个类似枚举的搜索尝试
过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就「回溯」
返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为「回溯点」
。
以
深度优先
的方式系统地搜索问题的解的算法称为回溯法
「通用解题法」
之称。基本做法是搜索
,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法
。
系统性
回溯法在问题的解空间树
中,按深度优先
策略,从根结点出发搜索解空间树。
跳跃性
算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过
对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树
,继续
按深度优先策略搜索。
图的深度优先遍历
回溯法与穷举查找是一样的吗?
可以把回溯法和分支限界法看成是穷举法的一个改进。该方法至少可以对某些组合难题的较大实例求解。
每次只构造侯选解的一个部分。然后评估这个部分构造解:如果加上剩余的分量也不可能求得一个解,就绝对不会生成剩下的分量
解向量
回溯法希望一个问题的解能够表示成一个n元式(x1,x2,…,xn)的形式。
对分量xi的取值范围的限定。
为满足问题的解而对不同分量之间
施加的约束。
对于问题的一个实例
,解向量满足显式约束
条件的所有多元组
,构成了该实例的一个解空间。
注意:同一问题可有
多种表示
,有些表示更简单,所需状态空间更小(存储量少,搜索方法简单)。
对于有n种可选物品的0-1背包问题
{(0,0,0),(0,0,1),(0,1,0),(0,1,1), (1,0,0),(1,0,1),(1,1,0),(1,1,1)}
用完全二叉树
表示的解空间
根节点
到叶节点
的路径定义了解问题的一个解定义
问题的解空间
;确定
易于搜索的解空间结构
;深度优先
方式搜索解空间
,并在搜索过程中用剪枝函数
避免无效搜索。约束函数
在扩展结点
处剪去不满足约束
的子树;限界函数
剪去得不到最优解
的子树。扩展结点R
,一旦产生了它的一个儿子C
,就把C
当做新的扩展结点
。完成
对子树C(以C为根的子树)的穷尽搜索
之后,将R重新变成扩展结点
,继续生成R的下一个儿子(如果存在)。算法只保存从根结点到当前扩展结点的路径
。h(n)
,则回溯法所需的计算空间通常为O(h(n))
。O(2h(n)
或O(h(n)!)
内存空间。如下图所示:
解空间一般用解空间树(Solution Space Trees,也称状态空间树)的方式组织,树的根结点位于第1层,表示搜索的初始状态,第2层的结点表示对解向量的第一个分量做出选择后到达的状态,第1层到第2层的边上标出对第一个分量选择的结果,依此类推,从树的根结点到叶子结点的路径就构成了解空间的一个可能解。
用完全二叉树表示的解空间(n=3
)
左边子树表示1号物品要放入背包 ,右边子树表示1号物品不放入背包 树中的8个叶子结点分别代表该问题的8个可能解。
例如,从跟结点到结点H的路径相应于解空间中元素(1,1,1)
又如:
当搜索到一个L结点时,就把这个L结点变成为E结点,继续向下搜索这个结点的儿子结点。当搜索到一个D结点,而还未得到问题的最终解时,就向上回溯到它的父亲结点。如果这个父亲结点当前还是E结点,就继续搜索这个父亲结点的另一个儿子结点;如果这个父亲结点随着所有儿子结点都已搜索完毕而变成D结点,就沿着这个父结点向上,回溯到它的祖父结点。这个过程继续进行,直到找到满足问题的最终解。
递归回溯
回溯法对解空间作深度优先搜索,因此,在一般情况下用递归方法实现回溯法,t表示搜索深度。
void backtrack (int t){
if (t >n ) { //t>n表示算法已搜索到叶节点
output(x); //记录或输出得到的可行解x
} else {
for (int i = f(n, t); i <= g(n, t); i++) {
//其中f(n,t),g(n,t)分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号。
x[t] = h(i); //h(i)表示在当前扩展节点处x[t]的第i个可选值
if (constraint(t) && bound(t))
backtrack(t + 1);
}
}
}
if (Constraint(t)&&Bound(t) ) backtrack(t + 1);
if语句含义:Constraint(t)和Bound(t)表示当前扩展节点处的约束函数和限界函数。
取值满足问题的约束条件
,否则不满足问题的约束条件,可剪去相应的子树目标函数不越界
,还需由backtrack(t+1)对其相应的子树做进一步搜索。否则,当前扩展节点处x[1:t]的取值是目标函数越界,可剪去相应的子树迭代回溯
采用树的非递归
深度优先遍历算法,可将回溯法表示为一个非递归迭代过程。
void iterativeBacktrack(){
int t =1 ;
while(t > 0) {
if (f(n,t)< = g(n,t)) {
for(int i = f(n,t); i <= g(n,t); i++) {
x[t] = h(i);
if (constraint(t) && bound(t)) {
if (solution(t)) {
output(x); //输出最优解
} else {
t++; //搜索下一层节点
}
}
}
} else {
t--; //回溯到上一节点
}
}
}
分析: