回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
什么情况下适合使用回溯法呢? 那先来看看适用回溯法的问题的一般描述:
可用回溯法求解的问题P,通常要能表达为:对于已知的由n元组(x1,x2,…,xn)组成的一个状态空间E={(x1,x2,…,xn)∣xi∈Si ,i=1,2,…,n},给定关于n元组中的一个分量的一个约束集D,要求E中满足D的全部约束条件的所有n元组。其中Si是分量xi的定义域,且 |Si| 有限,i=1,2,…,n。我们称E中满足D的全部约束条件的任一n元组为问题P的一个解。
解决这类问题最直接的办法就是枚举法,即对E中的所有n元组逐一地检测其是否满足D的全部约束,若满足,则为问题P的一个解。但显然,其计算量是相当大的。
回溯法从根结点出发,按照深度优先策略遍历解空间树,搜索满足约束条件的解。在搜索至树中任一结点时,先判断该结点对应的部分解是否满足约束条件,或者是否超出目标函数的界,也就是判断该结点是否包含问题的(最优)解,如果肯定不包含,则跳过对以该结点为根的子树的搜索,即所谓剪枝(Pruning);否则,进入以该结点为根的子树,继续按照深度优先策略搜索。
回溯法的求解过程
由于问题的解向量X=(x1, x2, …, xn)中的每个分量xi(1≤i≤n)都属于一个有限集合Si={ai1, ai2, …, airi},因此,回溯法可以按某种顺序(例如字典序)依次考察笛卡儿积S1×S2×…×Sn中的元素。初始时,令解向量X为空,然后,从根结点出发,选择S1的第一个元素作为解向量X的第一个分量,即x1= a11,如果X=(x1)是问题的部分解,则继续扩展解向量X,选择S2的第一个元素作为解向量X的第2个分量,否则,选择S1的下一个元素作为解向量X的第一个分量,即x1= a12。依此类推,一般情况下,如果X=(x1, x2, …, xi)是问题的部分解,则选择Si+1的第一个元素作为解向量X的第i+1个分量时,有下面三种情况:
(1)如果X=(x1, x2, …, xi+1)是问题的最终解,则输出这个解。如果问题只希望得到一个解,就结束搜索,否则继续搜索其他解;
(2)如果X=(x1, x2, …, xi+1)是问题的部分解,则继续构造解向量的下一个分量;
(3)如果X=(x1, x2, …, xi+1)既不是问题的部分解也不是问题的最终解,则存在下面两种情况:
① 如果xi+1= ai+1k不是集合Si+1的最后一个元素,则令xi+1= ai+1k+1,即选择Si+1的下一个元素作为解向量X的第i+1个分量;
② 如果xi+1= ai+1k是集合Si+1的最后一个元素,就回溯到X=(x1, x2, …, xi),选择Si的下一个元素作为解向量X的第i个分量,假设xi= aik,如果aik不是集合Si的最后一个元素,则令xi= aik+1;否则,就继续回溯到X=(x1, x2, …, xi-1);
在用回溯法求解问题时,常常遇到两种典型的解空间树:
(1)子集树(Subset Trees):当所给问题是从n个元素的集合中找出满足某种性质的子集时,相应的解空间树称为子集树。在子集树中,|S1|=|S2|=…=|Sn|=c,即每个结点有相同数目的子树,通常情况下c=2,所以,子集树中共有2n个叶子结点。因此,遍历子集树需要Ω(2n)时间。例如,0/1背包问题的解空间树是一棵子集树。
(2)排列树(Permutation Trees):当所给问题是确定n个元素满足某种性质的排列时,相应的解空间树称为排列树。在排列树中,通常情况下,|S1|=n,|S2|=n-1,…,|Sn|=1,所以,排列树中共有n!个叶子结点,因此,遍历排列树需要Ω(n!)时间。例如,TSP问题的解空间树是一棵排列树。
回溯法的适用性
- 回溯法的n个输入向量应该是能够独立判断是否属于解空间的,如果必须把所有的n个向量都检查后得到的一个值再去判断是否属于解空间,就成为了穷举法;
- 回溯法的n个输入向量需要固定,如果第一个向量选择后,第一个向量依然要参与选择,就会成为死循环。
http://wenku.baidu.com/view/eae2aa244b35eefdc8d33331.html
http://www.cnblogs.com/jillzhang/archive/2007/10/21/922830.html
http://www.cnblogs.com/drizzlecrj/archive/2007/10/04/913703.html