回溯法

1.问题的引入
要求找到一组解,或要求找到一个满足某些限制的最优解。通过彻底的搜索方法来解决:彻底的搜索,需要进行大量的比较、舍弃、运算时间为代价。因此,用穷举法解某些实际问题是不现实的,彻底搜索的运算量很大,有时大到计算机承受不了的程度。使用回溯法可以大大减少实际的搜索。例如,迷宫问题,八皇后问题,骑士周游世界问题。 关键是要找到回溯的条件。

算法思想:通过对问题的分析,找出一个解决问题的线索,然后沿着这个线索往前试探,若试探成功,就得到解,若试探失败,就逐步往回退,换别的路线再往前试探。实际上是广度与深度搜索结合的搜索,深度搜索过程中碰到条件不满足,则退回上一层,在每一层上也进行全面的搜索

2.回溯法的基本思想

回溯法是带优化的穷举法。回溯法的基本思想:在一棵含有问题全部可能解状态空间树上进行深度优先搜索,解为叶子结点。在回溯法中,并不是先构造出整棵状态空间树,再进行搜索,而是在搜索过程中逐步构造出状态空间树,即边搜索,边构造。回溯法在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。

1)如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。

2)否则,进入该子树,继续按深度优先的策略进行搜索。

回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束

回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

3.基本概念

扩展结点 :一个 正在产生儿子 的结点称为扩展结点
活结点 :一个自身已生成但其 儿子还没有全部生成 的节点称做活结点
死结点 :一个 所有儿子已经产生 的结点称做死结点
实例:
0-1
背包问题
n=3,C=30, w={16, 15, 15}, v={45, 25, 25}


开始时,Cr=C=30V=0A为唯一活结点,也是当前扩展结点;扩展A,先到达B结点;Cr=Cr-w1=14V=V+v1=45;此时AB为活结点,B成为当前扩展结点;扩展B,先到达C;Cr<w2C导致一个不可行解,回溯到B;再扩展B到达D;D可行,此时ABD是活结点,D成为新的扩展结点;扩展D,先到达E;Cr<w3E导致一个不可行解,回溯到D;再次扩展D到达F;由于F是叶结点,即得到一个可行解x=(1,0,0)V=45;

F不可扩展,成为死结点,返回到D;D没有可扩展结点,成为死结点,返回到B;B没有可扩展结点,成为死结点,返回到A;

A再次成为扩展结点,扩展A到达G;Cr=30V=0,活结点为AGG为当前扩展结点;扩展G,先到达H;Cr=Cr-w2=15V=V+v2=25,此时活结点为AGHH成为当前扩展结点;扩展H,先到达I;Cr=Cr-w3=0V=V+v3=50;I是叶结点,且50>45,皆得到一个可行解x=(0,1,1)V=50;I不可扩展,成为死结点,返回到H;

再扩展H到达J;J是叶结点,且25<50,不是最优解;J不可扩展,成为死结点,返回到H;H没有可扩展结点,成为死结点,返回到G;

再扩展G到达L;Cr=30V=0,活结点为AGLL为当前扩展结点;扩展L,先到达MM是叶结点,且25<50,不是最优解,又M不可扩展,返回到L;

再扩展L到达NN是叶结点,且0<50,不是最优解,又N不可扩展,返回到L;L没有可扩展结点,成为死结点,返回到G;G没有可扩展结点,成为死结点,返回到A

A没有可扩展结点,成为死结点,算法结束,最优解X=(0,1,1),最优值V=50

4.回溯法的应用步骤

  针对所给问题,定义问题的解空间

  确定易于搜索的解空间结构

  深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

常用剪枝函数

约束函数在扩展结点处剪去不满足约束的子树;

限界函数剪去得不到最优解的子树。

5.关于复杂性

回溯法的一个显著特征是在搜索过程中动态产生问题的解空间。在任何时刻,算法只保存从根结点到当前扩展结点的路径。

如果解空间树中从根结点到叶结点的最长路径的长度为h(n),则回溯法所需的计算空间通常为O(h(n))

子集树:从n个元素的集合找出满足某种性质的子集,相应的解空间树称为子集树。 例如0-1背包问题。遍历子集树O(2n)计算时间;

排列树:确定n个元素满足某种性质的排列,相应的解空间树称为排列树。排列树通常有n!个叶结点。因此遍历排列树需要O(n!)计算时间。例如8后问题、旅行售货员问题的解空间是一棵排列树。

6.经典例题:

6.1 回溯法求解0-1背包问题


解题的基本指导思想:

按贪心法的思路,优先装入价值/重量比大的物品。当剩余容量装不下最后考虑的物品时,再用回溯法修改先前的装入方案,直到得到全局最优解为止。

搜索解空间树策略:

只要其左儿子结点是一个可行结点,搜索就进入其左子树。约束函数

当右子树有可能包含最优解时才进入右子树搜索,否则将右子树剪去。限界函数

剪枝方法:

r:当前剩余物品价值总和

cv:当前获得价值;

bestp:当前最优价值。

cv+r<=bestp时,可剪去右子树。

计算右子树上界方法 :

剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。由此得到的价值是右子树中解的上界

<span style="color:#000000;">package Algorithm;

public class Bag01Trace {

	static int N=5;/*物品个数*/ 
	static int cv=0;/*当前价值*/
	static int cw=0;/*当前重量*/
	static int bestv=0;/*当前最优价值*/
	static int bestx[] = new int[N+1];/*当前最优解*/
	static int C=10;/*背包容量*/
	static int v[]={0,6,3,6,5,4};/*物品价值、重量。0元素不用*/
	static int w[]={0,2,2,4,6,5};
	static int x[]=new int[N+1]; //x[i]表示物品i当前是否加入背包

	public static void main(String[] args) {
		Backtrack(1);
	    putout(); 
	}

	private static void Backtrack(int i) {
		int j;
		if (i > N) /* 到叶结点 */
		{
			for (j = 1; j <= N; j++)
				bestx[j] = x[j];
			bestv = cv;
		} else {
			if (cw + w[i] <= C) /* 搜索左子树 */
			{
				x[i] = 1;
				cw += w[i];
				cv += v[i];
				Backtrack(i + 1);
				cw -= w[i];
				cv -= v[i];
			}
			if (Bound(i + 1) > bestv)/* 搜索右子树 */
			{
				x[i] = 0;
				Backtrack(i + 1);
			}
		}
	}

	private static int Bound(int i) {
		int left = C - cw; // 剩余容量
		int b = cv; // cv当前价值
		while (i <= N&&w[i] <= left) {
			b += v[i];
			left -= w[i];
			i++;
		}
		/* 装满背包 */
		if (i <= N)
			b += left * v[i] / w[i];
		return b;
	}

	private static void putout() {
		System.out.print("放入背包的物品是:");
		cw = 0;
		for (int i = 1; i <= N; i++)
			if (bestx[i] == 1) {
				System.out.print(i + "   ");
				cw += w[i];
			}
		System.out.println();
		System.out.print("放入背包物品总重为:" + cw + "   ");
		System.out.print("最大价值和为:" + bestv);
	}

}</span>

结果:

放入背包的物品是:1   2   3   
放入背包物品总重为:8   最大价值和为:15


6.2 8皇后问题:http://blog.csdn.net/guoxiaowei400/article/details/40356407




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值