通用的回溯法的算法框架

开篇

算法部分已经更新了动态规划和贪心算法,接下来这一部分将会是又一经典算法——回溯法。
仍然会有大量的习题等着我去琢磨去更新博客,想想虽然挺受折磨,但算法这东西你懂的,学会了的感觉更好。

回溯法介绍

1.问题的解空间
用回溯法求解问题时,应明确定义问题的解空间。问题的解空间至少应包含问题的一个(最优)解。例如,对于0-1背包问题这个经典的问题,我们已经用动态规划法解释过一次,其实用回溯法也可以很好的解决。对于有n种可选择的物品的0-1背包问题,其解空间由长度为n的0-1向量组成。该解空间包含对变量的所有可能的0-1赋值。当n=3时,其解空间如下。
{(0,0,0),(0,1,0),(0,0,1),(1,0,0),(0,1,1),(1,0,1),(1,1,0),(1,1,1)}
定义了问题的解空间后,还应将解空间很好地组织起来,使得能用回溯法方便地搜索整个解空间。通常将解空间组成树或图的形式。
通常组成为子集树或者排列树,我们后文会给出具体形式。
2.回溯法的基本思想
先明确几个概念:
扩展结点:一个正在搜索儿子的结点。
死结点:不能再向纵深扩展的结点。
活结点:与死结点相对,可以向纵深扩展的结点。
确定了解空间的组织结构后,回溯法从开始结点(根节点)出发,以深度优先方式搜索整个解空间。这个开始节点成为活结点。同时成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动,即回溯,至最近的一个活结点处,并使这个活结成为当前的扩展结点。回溯法以这种工作方法递归地在解空间中搜索,直至找到所要求的解或解空间中已无活结点为止。
以上方法就可以帮助我们将解空间变成一个树的结构,我们就是在解空间中不断向下找新的活结点,直到碰到死结点的时候,我们再回到上面的活结点处。
树的剪枝
剪枝:搜索过程中使用约束函数可避免无意义的搜索,那些不满足约束条件的子树。
约束函数 分为显约束和隐约束。
如果是最优化问题,还可以用限界函数,剪去那些不可能含有最有答案结点的子树。
解空间种类
解空间一般分为排列树子集树。
排列树:解n元组定长,树中从根到叶子结点路径为解空间中一个解,代表问题TSP旅行商问题。
子集树:解n元组不定长,这种解空间最具代表性的问题就是0-1背包问题
遍历子集树的时间复杂度为O(2^n)
因为如果树的深度为n,则叶子结点树为2^(n-1)
而遍历排列树的时间复杂度为O(n!)
子集树

排列树

回溯模板

递归回溯

void Backtrack(int t)
{
	//t表示递归深度,即当前扩展结点在解空间树中的深度
	if(t > n)
		Output(x);
	else
	{	//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))
			{
				Backtrack(t+1)'
			}
		}
	}
}

n用来控制递归深度,当t>n时,算法已搜索到叶节点。
迭代回溯

void IterativeBacktrack(void)
{
	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(Constrainit(t)&&Bound(t))
				{
					if(Solution(t))
						Output(x);
					else
						t++;
				}
				else
					t--;
			}
		}
	}
}

子集树回溯模板

void Backtrack(int t)
{
	if(t>n)
		Output(x);
	else
	{
		for(int i = 0;i <= 1;i++)
		{
			x[t] = i;
			if(Constraint(t)&&Bound(t))
				Backtrack(t+1);
		}
	}
}

排列树回溯

void Backtrack(int t)
{
	if(t > n)
		Output(x);
	else
	{
		for(int i = t;i <= n;i++)
		{
			Swap(x[t],x[i]);
			if(Constraint(t)&&Bound(t))
				Backtrack(t+1);
			Swap(x[t],x[i]);
		}
	}
}

在调用Backtrack(1)执行回溯搜索前,先将变量数组x初始化为单位排列(1,2,…,n)。
这里要注意一下,我们只是在一维数组中去模拟多种树形遍历的可能性,并没有在树中遍历。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值