参考链接:
- http://www.cnblogs.com/chinazhangjie/archive/2010/10/22/1858410.html
- http://baike.baidu.com/link?url=m-I1xz9PSYf9Hr8koWBcx2JNnHVCmKT_wA4L4ryrqHocN-I6oTp-pnEl9HB3FhhT8Gr2J50mG-aNRmF6K5QHbnO3_GdQOFM7lxBvUUc0zfOnDVhjvNEToqqY8Xeng9zC
一、回溯法原理
回溯法(探索与回溯法)是一种选优探索法,又称为试探法,按选优条件向前探索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术称为回溯法,而满足回溯条件的某个状态的点成为“回溯点”。
基本思想:
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根节点出发深度搜索解空间树。当探索到某一节点时,要先判断该节点是否包含问题的解,如果包含,就从该节点出发继续探索下去,如果该节点不包含问题的解,则逐层向其祖先节点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。若用回溯法求问题的所有解时,要回溯到根,且根节点的所有可行的子树都要已被搜索遍才结束。而若使用回溯法求任一个可行解时,只要搜索到问题的一个解就可以结束。
思想阐述:
可用回溯法求解的问题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的一个解。
解问题P的最朴素的方法就是枚举法,即对E中的所有n元组逐一地检测其是否满足D的全部约束,若满足,则为问题P的一个解。但显然,其计算量是相当大的。
回溯法与枚举法的不同在于回溯法可以利用约束条件或限界条件剪枝。回溯法从根节点开始按深度优先搜索策略形成的树成为状态空间树,状态空间树中的每一个节点成为状态节点,每一个叶子节点成为解状态节点,每一个满足所有约束条件或限界条件的叶子节点成为回答状态节点,对应问题的一个解。回溯法在进行深度优先搜索时到达一个状态节点,如果其不满足约束条件或限界条件,那么这个状态节点继续往下深度优先搜索的所有状态也都不会满足约束条件,因此这个状态节点以后的状态节点都不用再考虑,这就是利用约束条件或限界条件进行剪枝的原理,也是回溯法相比枚举法减少计算复杂度的方法。
回溯法解题的一般步骤:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
注意每次进行回溯时一定要恢复当前状态节点的状态。比如状态节点 v 有两个子节点 x 和 y, 从状态节点v出发深度优先搜索子节点 x ,当 x及其子孙节点搜索完毕回溯到状态节点 v 时, 需要将遍历 x 子孙节点时改变的状态都还原至 v 时的状态。
二、回溯法应用
1.01背包问题。
01背包问题描述:有编号分别为a,b,c,d,e的五件物品 n=5,它们的重量分别是w1=2,w2=2,w3=6,w4=5,w5=4,它们的价值分别是v1=6,v2=3,v3=5,v4=4,v5=6,每件物品数量只有一个,现在给你个承重为C=10的背包,如何让背包里装入的物品具有最大的价值总和?
01背包问题更常用的解法是动态规划算法,其时间复杂度为 O(nC), 空间复杂度为O(nC),动态规划算法解决01背包问题解法详细描述参见:http://blog.csdn.net/na_beginning/article/details/62884939
这里我们使用回溯法也可以解决01背包问题,首先定义问题解空间,从物品 a 开始考虑,存在两种选择,背包放入 a (用1做标记)和不放入 a (用 0 做标记),然后一次考虑其他四件物品,分别都有两种选择,放入和不放入,可以构建一个子集树,这棵树就是状态空间树,每一条从根节点 a 到叶子结点的路径构成了解空间。按深度优先策略从根节点 a 搜索整棵树,先考虑放入的情况,再考虑不放入的情况,没达到一个状态都需要判断当前状态中是否满足约束条件—当前背包中已放入物品的总重量是否超过背包能容纳的重量 C,如果不满足约束条件就回溯,回溯时要注意一定要恢复状态。
回溯法解决01背包问题的时间复杂度是 O(2^n), 因为每一件物品都需要考虑放入与不放入两种情况,实际复杂度会比这个小,因为中间状态不满足约束条件时存在剪枝;空间复杂度是O(n),因为递归需要用到栈,栈的深度是O(n),即空间搜索树的高度,而且保存搜索路径时也需要用到O(n)长度的数组。
代码: