算法分析与设计CH5回溯法

目录

思维导图

回溯算法思想及框架

适用条件 

基本步骤 

例题

回溯算法的效率的影响因素 

思维导图

 

回溯算法思想及框架

1. 结点

  • 活结点:一个自身已生成但其儿子还没有全部生成的结点称作活结点
  • 死结点:一个所有儿子已经产生的结点
  • 扩展结点:一个正在生成儿子的结点

 2.深度优先

  • 如果对一个扩展结点R,一旦产生了一个儿子C,就把C当作新的扩展结点,R变成活结点。在完成对子树C(以C为根的子树)的穷尽搜索之后,将R重新变成扩展结点,继续生成R的下一个儿子(如果存在),若R的所有儿子结点都已产生,R变成死结点

 3.回溯法

  • 当需要找出问题的解集或者要求满足某些约束条件的最优解,常使用回溯法;回溯法的基本方法就是搜索,是一种组织井井有条的,能避开不必要搜索的穷举式搜索法;适用于解一些组合数相当大的问题
  • 回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索整个解空间树;算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解:不包含,跳过,继续该结点兄弟结点的搜索;否则进入子树,继续按深度优先策略搜索
  • 类似于树的深度遍历,但是有本质区别(数据结构中树的深度遍历方法,先创建树,再深度遍历)
  • 问题的解向量:回溯法希望一个问题的解能够表示成一个n元组(x1,x2,...xn)
  • 显约束:对分量xi的取值限定
  • 隐约束:为满足问题的解而对不同分支施加约束
  • 解空间的组织形式:子集树(所给问题是从N个元素的集合S中找出满足某种性质的子集),排列树(所给问题时确定n个元素满足某种性质的排列)
  • 在搜索过程中动态产生问题的解空间树,即边搜索边扩展分支;在任何时刻,算法中只保存从根结点到当前扩展结点的路径。解空间树从根结点到叶结点的最长路径的长度为h(n),则回溯法所需的计算空间通常为O(h(n)),存储整个解空间子集树需要O(2^h(n)),排列树需要O(h(n)!)的内存空间。
  • 4.递归回溯算法框架

参数t表示递归深度,即当前扩展结点在解空间树中的深度。n用来控制递归深度,当t>n时,算法已搜索到叶结点。此时,由Output(x)记录或输出得到的可行解x。算法 backtrack 的for循环中f(n, t)和g(n, t)分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号。h(i)表示在当前扩展结点处x[t]的第i个可选值。Constraint(t)和Bound(t)表示在当前扩展结点处的约束函数和限界函数。Constraint(t)返回的值为true时,在当前扩展结点处x[1:t]的取值满足问题的约束条件,否则不满足问题的约束条件,可剪去相应的子树。Bound(t)返回的值为true时,在当前扩展结点处x[1:t]的取值未使目标函数越界,还需由Backtrack(t+1)对其相应的子树做进一步搜索。否则,当前扩展结点处x[1:t]的取值使目标函数越界,可剪去相应的子树。执行了算法的for循环后,已搜索遍当前扩展结点的所有未搜索过的子树。Backtrack(t)执行完毕,返回t-1层继续执行,对还没有测试过的x[t-1]的值继续搜索。当t=1时,若已测试完x[1]的所有可选值,外层调用就全部结束。显然,这一搜索过程按深度优先方式进行。调用一次Backtrack(1)即可完成整个回溯搜索过程。 

5.迭代回溯算法框架

 

6.子集树与排列树的回溯算法框架

 

适用条件 

基本步骤 

1.(简洁版)针对所给问题,定义问题的解空间;

2.确定易于搜索的解空间结构(树);

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

  • 常用剪枝函数:用约束函数在扩展结点处剪去不满足约束的子树;用限界函数剪去得不到最优解的子树

例题

1.n后问题

  • 问题描述:在n*n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于,在n*n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。以4叉树为例
  • 问题的解空间树及关键代码(n叉树)

  • 排列树

2.01背包问题

  • 问题描述:现有 N NN 件物品和一个容量为 C CC 的背包。放入第 i   ( 1 … N )  件物品耗费的空间是 w i ,得到的价值是 v i 。求解将哪些物品装入背包可使价值总和最大。
  • 解向量:n维0-1向量<x1,x2,...xn>,xi=1表示物品i选入背包
  • 解空间状态树:0-1取值的二叉树(子集树),有2^n片树叶
  • 可行性约束条件:
  • 限制函数:一般分数背包问题的贪心
  • 部分核心代码:
    //回溯函数
    void backtrack(int i){
        doublebound(int i);
        if(i>n){
           bestp=cp;
           return;
        }
        if(cw+w[i]<=c){
           cw+=w[i];
           cp+=v[i];
           put[i]=1;
           backtrack(i+1);
           cw-=w[i];
           cp-=v[i];
        }
        if(bound(i+1)>bestp)//符合条件搜索右子数
            backtrack(i+1);
    }
    //计算上界函数
    double bound(int i){
        doubleleftw= c-cw;
        double b =cp;
        while(i<=n&&w[i]<=leftw){
           leftw-=w[i];
           b+=v[i];
           i++;
        }
        if(i<=n)
           b+=v[i]/w[i]*leftw;
        returnb;
    }

3.装载问题

  • 问题描述:一批集装箱共n个要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi且W1+W2+……+Wn<=c1+c2;试确定一个合理的装载方案使这n个集装箱装上这两艘轮船。
  • 解向量:x{1,2,...n},x1=1表示装入到c1集装箱
  • 解空间状态树:子集树
  • 可行性约束条件:\sum_{t=1}^{i}wtxt\leq c1
  • 上界函数(右分支):当前载重量cw+剩余集装箱的量r<=当前最优载重量bestw
  • 最优装载方案:首先将第一艘轮船尽可能装满;将剩余的集装箱装上第二艘轮船
  • 部分核心代码:
void backtrack(int i){//搜索第i层结点 
	if(i>n){//到达叶结点 
		更新最优解bestx,bestw;
		return ; 
	}
	r -= w[i];//r初值为全部集装箱重量之和 
	if(cw+w[i]<=c){//搜索左子树 
		x[i]=1;
		cw += x[i];
		backtrack(i+1);
		cw -= w[i];
	} 
	if(cw+r>bestw){//搜索右子树 
		x[i]=0;
		backtrack(i+1);
	}
	r += w[i];
}

4.图的着色问题

  • 问题描述:给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。
  • 解向量:(x1,x2,...,xn)表示顶点i所着颜色xi
  • 可行性约束条件:顶点i与已着色的相邻顶点颜色不重复
  • 注意:在邻接图g[][]中,g[i][j]=g[j][i]
  • 部分核心代码:
    void backtrack(int t){//搜索第i层结点 
    	if(i>n){
    		sum++;
    	}
    	else{
    		for(int i=1; i<=m; i++){
    			x[t]=i;
    		}
    		if(ok(t)) backtrack(t+1);
    	}
    }
    boolean ok(int k){//检查颜色可用性 
    	for(int j=1; j<k; j++){
    		if(a[k][j] && (x[j]==x[k])) return false;
    	}
    	return true;
    }

5.旅行售货员问题

  • 有n个城市,找从一个城市出发走遍n个城市的最短回路问题
  • 解向量:{x1,x2,...,xn},xi表示走的第i个城市是xi
  • 解空间树:排列树
  • 约束条件:存在点k-1到当前搜索点k的通路,并且加上到k的路径长度小于当前最短路径长度
  • 部分核心代码:
    void dfs(int k){
    	if(k>n){ 
            if(g[x[n]][1]!=rmax && c+g[x[n]][1]<bestc){
                for(int i=2; i<=n; i++){
    				bestx[i]=x[i];
    		    }
    			bestc=c+g[x[n]][1]; 
    		}
    		return ;
    	}
    	else{
    		for(int i=k; i<=n; i++){//是否可以进入x[i]子树
    			if(g[x[k-1]][x[i]]!=rmax && (c+g[x[k-1]][x[i]]<bestc)){
    				swap(x[k],x[i]);
    				c=c+g[x[k-1]][x[k]];
    				dfs(k+1);
    				c=c-g[x[k-1]][x[k]];
    				swap(x[k],x[i]);
    			}
    		}
    	}
    }

6.最大团问题 

  • 问题描述:给定无向图G=(V,E),其中V是顶点集;E是V边集。如果U属于V,且对任意两个顶点u,v∈U有(u,v)∈E,则称U是G的完全子图。G的完全子图U是G的一个团当且仅当U不包含在G的更大的完全子图中。G的最大团是指G中所含顶点数最多的团。如果U属于V,且对任意u,v∈U有(u,v)不属于E,则称U是G的空子图。G的空子图U是G的独立集当且仅当U不包含在G的更大的空子图中。G的最大独立集是G中所含顶点数最多的独立集。对于任一无向图G=(V,E),其补图G'=(V',E')定义为:V'=V,且(u,v)∈E'当且仅当(u,v)∉E。如果U是G的完全子图,则它也是G'的空子图,反之亦然。因此,G的团与G'的独立集之间存在一一对应的关系。特殊地,U是G的最大团当且仅当U是G'的最大独立集。
  • 解向量:x{1,...i}
  • 解空间:子集树
  • 可行性约束条件:顶点i到已选入的顶点集中每一个顶点都有边相连
  • 上界函数:有足够多的可选择顶点使得算法有可能在右子树中找到更大的团
  • 部分核心代码:
    void Backtrack(MCP &G,int i){
        if (i>G.v){        //output()阶段
            for (int j=1; j<=G.v; j++)
                G.bestx[j] = G.x[j];    //记录最优解
            G.bestn =G.cnum;
            return ;
        }
        //检查顶点i与当前团的连接
        int OK = 1;
        for (int j=1; j<=i ; j++)
            if (G.x[j]&& G.a[i][j]==0){        //G.x[j]:顶点j在当前解的最大团内;G.a[i][j]:待考察i顶点与最大团中前i-1个顶点间边的关系
                //i不与j相连
                OK = 0;
                break;
            }
            if (OK) {        //进入左子树
                G.x[i] = 1;//把i加入团
                G.cnum++;
                Backtrack(G,i+1);
                G.x[i]=0;
                G.cnum-- ;
            }
            if (G.cnum+G.v- i>G.bestn){        //进入右子树——剪枝函数
                G.x[i] = 0;
                Backtrack(G,i+1);
            }
    }

回溯算法的效率的影响因素 

  • 产生x[k]的时间;满足显约束的x[k]的个数;计算约束函数的时间;计算上界的时间;满足约束条件和上界函数约束的所有x[k]的个数

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值