第5章-回溯法-复习

合集下载

回溯法课程知识点总结

回溯法课程知识点总结

回溯法课程知识点总结在回溯法中,通常使用递归的方式来遍历解空间树,每次遍历到下一层时,都会尝试选择一个决策。

如果选择的决策不满足约束条件,则进行回溯,取消该决策,重新选择其他决策。

当所有的决策都尝试完毕后,就回到上一层继续尝试其他决策,直至搜索到满足约束条件的解,或者搜索完整个解空间树。

回溯法的优点是能够有效地遍历解空间树,找到满足约束条件的解。

它也具有灵活性高、适用范围广等优点。

但同时,回溯法也存在着时间复杂度高、搜索空间大等缺点。

在实际应用中,回溯法通常需要结合具体问题进行适当地优化,以提高搜索效率。

下面我们将介绍回溯法的具体实现和应用。

1. 回溯法的实现回溯法的实现通常由两部分组成:递归函数和决策函数。

递归函数用于遍历解空间树,决策函数用于判断是否满足约束条件和进行决策选择。

下面以求解八皇后问题为例,介绍回溯法的实现。

八皇后问题是一个经典的回溯法应用题目,在一个8×8的棋盘上摆放八个皇后,使得它们互相不攻击。

互相不攻击的条件是:任意两个皇后不在同一行、同一列或同一斜线上。

```pythondef solve_n_queens(n):res = []def backtrack(path):if len(path) == n:res.append(path[:])returnfor i in range(n):if is_valid(path, i):path.append(i)backtrack(path)path.pop()def is_valid(path, col):row = len(path)for i in range(row):if path[i] == col or abs(row - i) == abs(col - path[i]):return Falsereturn Truebacktrack([])return res```在上面的代码中,solve_n_queens函数用于求解八皇后问题,其实现思路如下:首先,定义一个回溯函数backtrack,用于遍历解空间树。

回溯法

回溯法

西安邮电大学计算机学院
第5章 回溯法
以示例图为例,当 n = 4 时,假定驻地为节点 1,则旅行售货员问题的解空间是:
{ ( 1,2,3,4,1 )、 ( 1,2,4,3,1 )、 ( 1,3,2,4,1 )、 ( 1,3,4,2,1 )、 ( 1,4,2,3,1 )、 ( 1,4,3,2,1 ) },共有(4 - 1)!= 3!= 6 种可能。 当节点数为 n 时,有 (n - 1)!种可能的解(这是一个排列问题)。
计算机科学的先驱、英国科学家阿兰· 麦席森· 图灵
第5章 回溯法
回溯法以深度优先的方式搜索解空间。如果回溯法在执行过程
中判断解空间树的某个节点不包含问题的解时,则跳过对以 该节点为根的子树的搜索(子树中一定不会包含问题的解), 逐层向其祖先节点回溯;否则进入该子树,继续按深度优先策 略搜索。这也是“回溯法”名称的由来。
西安邮电大学计算机学院
第5章 回溯法
5.1.4 迭代回溯
采用树的非递归深度优先遍历算法,可将回溯法表示为一个非递归迭代过程。 void IterativeBacktrack( ) { 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 ( Constraint( t ) && Bound( t ) ) { if ( Solution( t ) ) output( x ); else t++; } } else t--; } 回溯法
(2)解空间结构 定义了问题的解空间后,还应将解空间很好地组织起来,使得能用回溯法方便地 搜索整个解空间。通常将解空间组织成树或图的形式。如果将解空间组织成树的

第5章 回溯算法

第5章   回溯算法

第5章 回溯算法
当我们确定了解空间的组织结构以后,回溯算法便可以 从起始结点(根结点)出发,以深度优先方式搜索整个 解空间。于是,这个起始结点既成为活结点,同时又成 为当前的扩展结点。在当前的扩展结点处,搜索过程就 向着纵深方向移动到一个新的结点,而这个新的结点就 成为当前的一个新的活结点,并且成为当前的扩展结点。 如果在当前的扩展结点处不能再向纵深方向移动,那么 当前的这个扩展结点就成为死结点。此时,就不能继续 纵深下去,应立即往回移动(这就是回溯)到最近的一 个活结点处,并且使得这个活结点成为当前的扩展结点。 回溯算法就是以这种方式递归地在解空间中进行不断地 搜索活动,直到找出所需要的解或者解空间中已经不再 有活结点时为止。
第5章 回溯算法
例如,当我们在求解0/1背包问题时,通常在使 用回溯算法的同时采用剪枝函数剪除导致不可行 解的子树。在使用回溯算法求解旅行商问题时, 如果从根结点到当前的扩展结点处的部分周游路 线的成本已经超过了当前找到的周游路线成本, 那么就可以判定以此结点为根结点的子树中不包 含最优解,因此,可以将该子树剪除。

迭代回溯


0/1背包问题
回溯算法求解的经典问题
装箱问题
最大通信团体问题
第5章 回溯算法
回溯算法具有通用的解题算法之称。使用回溯算法可
以系统地搜索一个问题的全部解或者其中的任意一个解。 回溯算法是一个既具有系统性,同时又具有跳跃性的搜 索算法。它在问题的解空间树中,往往可以根据深度优 先策略,由根结点出发依次搜索整棵解空间树。当回溯 算法在搜索到解空间树中的任意一个结点时,应首先判 断该结点是否包含原问题的解。如果包含,就直接进入 到该子树,并且继续按照深度优先策略搜索问题的解; 如果不包含,那么就跳过对以此结点为根结点的子树的 搜索,并且依次逐层向其祖先结点回溯。当我们使用回 溯算法求一个问题的全部解时,通常要回溯到解空间树 的根结点,并且当根结点的所有子树都已经被搜索或者 遍历了一遍之后方能结束。然而,如果当只需要使用回 溯算法求问题的一个解时,通常只需要搜索到问题的一 个解就可以结束。这种以深度优先系统搜索问题的解的 算法称为回溯算法,它适用于求解数据规模比较大的问 题。

2020-wfx-第5章 回溯法-概述

2020-wfx-第5章 回溯法-概述

//产生一个可能的解分量
法 框

//其他操作
if (constraint(i) && bound(i))
backtrack(i+1); //满足约束条件和限界函数,继续下一层
ห้องสมุดไป่ตู้

}
}
}
(2)解空间为排列树
int x[n];
//x存放解向量,并初始化
void backtrack(int i)
//求解排列树的递归框架
{ if(i>n)
//搜索到叶子结点,输出一个可行解
输出结果;
else
{ for (j=i;j<=n;j++)
//用j枚举i所有可能的路径
{…
//第i层的结点选择x[j]的操作
swap(x[i],x[j]);
//为保证排列中每个元素不同,通过交换来实现
if (constraint(i) && bound(i))
3
{2,3,1}
1
{2,3,1}
2
{3,2,1}
1
{3,2,1}
1
{3,1,2}
2
{3,1,2}
回溯法的求解过程
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点 (开始结点)出发搜索解空间树。
首先根结点成为活结点,同时也成为当前的扩展结点。 在当前的扩展结点处,搜索向纵深方向移至一个新结点。如果在当前的扩 展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时应往回 移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。 递归地在解空间中搜索,直至找到所要求的解或解空间中已无活结点为止。

算法第5章回溯法

算法第5章回溯法

A C此r=时CAr-、w1B=为14活,结V=点V,+vB1=成4为5 当前扩展结点
扩展B,先到达D 再扩• 展CBr<到w达2,ED导致一个不可行解,回溯到B
Cr=C=30,V=0
• E可行,此时A、B、E是活结点,E成为新的扩展结点
• 扩展E,先到达J
B w =16,v =45 C C =30,V=0 • 再次––扩展C由r<E于w到K3,达是JK叶导1结致点一,个即不得可到行一1解个,可回行溯解到xE=(1,0,0),V=45
3

J 不可行解 V=45 C =0,V=50 再扩•• 展C扩Cr=展到3G0达,,GV先=到0,达活N结,点N是为叶A、结C点、,G且,2G5<为5当0,前不扩是展最结优点解,又N不可r扩展,返回到G
x=(1,0,0) 50>45 • 再扩展G到达O,O是叶结点,且0<50,不是最优解,又O不可扩展,返回到G
if (legal(t)) backtrack(t+1); swap(x[t], x[i]);//回溯还原
}
}
回溯法搜索
排列树的一
般算法
5.2 装载问题
有n个集装箱要装上2艘n载重量分别为c1和c2的轮船,其中集
装箱i的重量为wi,且 wi c1 c2 i 1
✓问题:是否有一个合理的装载方案,可将这n个集装箱装上这2
不可行解 V=45 – 25<50 –
CL是r=C叶r-结w3点=0,,且V5=0V>+4v53=,5皆0 得到一个可行解x=(0,1,1),V=50
M – L不可扩展,成为死结点,返回到F
不是 • 再扩展F到达M

– M是叶结点,且25<50,不是最优解

第5章 回溯法

第5章  回溯法
a(1)*m2*m3+a(4)*m1*m3=a(7)*m1*•m2成立。
设置中间变量g:先赋值g=1;若出现某两数字相同(即 a(i)=a(k))或a(1)>a(4),则赋值g=0(重复标记)。 首先从a(1)=1开始,逐步给a(i)(1≤i≤9)赋值,每一 个a(i)赋值从1开始递增至9。直至a(9)赋值,判断: 若i=9,g=1,a(1)*m2*m3+a(4)*m1*m3=a(7)*m1*m2•同时 满足,为一组解,用n统计解的个数后,格式输出这组解。 若i<9且g=1,表明还不到9个数字,则下一个a(i)从1开 始赋值继续。 若a(9)=9,则返回前一个数组元素a(8)增1赋值(此时, a(9)又从1开始)再试。若a(8)=9,则返回前一个数组元素 a(7)增1赋值再试。依此类推,直到a(1)=9时,已无法返 回,意味着已全部试毕,求解结束。
5.2 桥本分数式
案例提出:


日本数学家桥本吉彦教授于1993年10月在我国山东举行的中日美三 国数学教育研讨会上提出以下填数趣题:把1,2,„,9这9个数字填入 下式的9个方格中(数字不得重复),使下面分数等式成立:

□ □ □ ── + ── = ── □□ □□ □□
桥本教授当即给出了一个解答。这一填数趣题的解是否唯一?如果 不唯一究竟有多少个解?试求出所有解答 (等式左边两个分数交换次 序只算一个解答)。•
(4) 回溯实现



回溯法的试探搜索,是一种组织得井井有条的、能避免一些不必 要搜索的枚举式搜索。回溯法在问题的解空间树中,从根结点出 发搜索解空间树,搜索至解空间树的任意一点,先判断该结点是 否包含问题的解;如果肯定不包含,则跳过对该结点为根的子树 的搜索,逐层向其父结点回溯;否则,进入该子树,继续搜索。 从解的角度理解,回溯法将问题的候选解按某种顺序进行枚举和 检验。当发现当前候选解不可能是解时,就选择下一个候选解。 在回溯法中,放弃当前候选解,寻找下一个候选解的过程称为回 溯。若当前候选解除了不满足问题规模要求外,满足所有其他要 求时,继续扩大当前候选解的规模,并继续试探。如果当前候选 解满足包括问题规模在内的所有要求时,该候选解就是问题的一 个解。 通过例5-1“4皇后问题的回溯”理解回溯过程的实现。

第五章 回溯法

第五章  回溯法

• Cr=C=30,V=0
C为容量,Cr为剩余空间,V为价值。 • A为唯一活结点,也是当前扩展结点。
H D 1 0 I 1
1 B 0 E 1 0 J K
A
0 C 1 F 1 0 L M N 0 G 1 0 O
5.1 回溯法的算法框架
• n=3, C=30, w={16,15,15}, v={45,25,25}
理论上
寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个, 在检查完所有或部分候选解后,即可找到所需要的解。
但是
当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方
法是可行的。
若候选解的数量非常大(指数级,大数阶乘),即便采用最快的计算机也只能 解决规模很小的问题。
显约束
对分量xi的取值限定。
隐约束 为满足问题的解而对不同分量之间施加的约束。
5.1 回溯法的算法框架
解空间(Solution Space)
对于问题的一个实例,解向量满足显式约束条件的所有多元组,构成了该 实例的一个解空间。 注意:同一问题可有多种表示,有些表示更简单,所需状态空间更小(存储 量少,搜索方法简单)。
回溯法引言
以深度优先的方式系统地搜索问题的解的算法称为回溯法 使用场合
对于许多问题,当需要找出它的解的集合或者要求回答什么解是满足某些
约束条件的最佳解时,往往要使用回溯法。 这种方法适用于解一些组合数相当大的问题,具有“通用解题法”之称。 回溯法的基本做法 是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。
一个正在产生儿子的结点称为扩展结点
活结点(L-结点,Live Node)
一个自身已生成但其儿子还没有全部生成的节点称做活结点

第五章 回溯法1--基本概念--n后问题

第五章    回溯法1--基本概念--n后问题
1 6 4 3 20 4 3 30 5 A A 2 10 2 1
B
3 4
C
4 G G 3 M M 2 H H 4 N N
D D
4 26>25 2 I I 3 P P 2 J J
E
3
图1 四个顶点的带权图
F
4 L L 59
K
2 Q Q
O
60+6>59 25
第五章 回溯法
19+6=25 29+30>25
活结点 当前扩展结点 死结点 D B 1 A
0 1 F 0
K
45
1
0
E
C 0 G 0 1 N
25
死结点 1
1
H
0
I
1
J
0 O
0
4
L
50
M
25
图5-2 在0-1背包问题的解空间树上搜索最优解
第五章 回溯法
例2:旅行售货员问题:某售货员要到若干城市推销商 品,已知各城市间的路程(或旅费)。他要选一条从 驻地出发,经过每个城市一遍,最后回到驻地的路线 使总的路程(总的旅费)最小。
第五章 回溯法 2
2. 解空间树
通常把解空间组织成解空间树。 例如:对于n=3时的0-1背包问题,其解空间用一 棵完全二叉树表示,如下图所示。
1
A
0
B
1 0 1
C
0
D 1 H
0 1
E
0
F
1 0 1
G
0
I
J
K
L
M
N
O
0-1背包问题的解空间树
第五章 回溯法
3
3. 搜索解空间树的过程
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
20
批处理作业调度
解空间:排列树 class Flowshop { friend int Flow(int M[][2], int , int [ ]); private: void Backtrack(int i); int m[ ][3], //各作业所需处理时间,下标0没有使用 *x, //当前作业调度 *bestx, //当前最优作业调度 *f2, //机器2完成处理时间 f1, //机器1完成处理时间 f, //完成时间和 4 bestf, //当前最优值 n; //作业数 }; 21
5
问题的解空间
问题的解向量:回溯法希望一个问题的解能够表示成一 个n元式(x1,x2,…,xn)的形式。 显约束:对分量xi的取值限定。 隐约束:为满足问题的解而对不同分量之间施加的约束。 解空间:对于问题的一个实例,解向量满足显式约束条 件的所有多元组,构成了该实例的一个解空间。 注意:同一个问题可以有多种表示,有些表示方法更 简单,所需表示的状态空间更小(存储量少,搜索方 法简单)。
法的吗? 9
排列树
遍历排列树需要Ω(n!)计算时间 void backtrack (int t) { //t为树的深度,根为1 if (t>n) output(x); else for (int i=t;i<=n;i++) { swap(x[t], x[i]); //重新组合 if (legal(t)) backtrack(t+1); swap(x[t], x[i]); //取消该组合 } }
第5章 回溯法
通过应用范例学习回溯法的设计策略
(1)装载问题; (2)批处理作业调度; (3)符号三角形问题 (4)n后问题; (5)0-1背包问题; (6)最大团问题; (7)图的m着色问题; (8)旅行售货员问题; (9)圆排列问题; (10)电路板排列问题; (11)连续邮资问题。
2
问题的解空间
n后问题
在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象 棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线 上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后, 任何2个皇后不放在同一行或同一列或同一斜线上。
1
1 2 3 4 5 Q Q
2
3
4
Q
5
6
Q
7
8
解向量:(x1, x2, … , xn) 显约束:xi=1,2, … ,n 隐约束: 每列的值 不一样 1)不同列:xixj 2)不处于同一正、反对角线: |i-j||xi-xj|
Q
6
7 8 X
Q
Q Q 6 4 7 1 8 2 5 3
25
n后问题
class Queen{ friend int nQueen(int); private: bool Place(int k); void Backtrack(int t); int n, //皇后个数 *x; //当前解 long sum; //当前已经找到的可行方案数 };
6
旅行售货员问题
某售货员要到若干城市去推销商品,已知各城市之 间的路程(或旅费),要求我们为他选定一条从驻 地出发,经过每个城市仅有 一次,最后回到驻地的 路线,使总路程(或总旅费)最小。 1
6 4 30 5 10
2
4
3
20
4
7
递归回溯
回溯法对解空间作深度优先搜索,在一般情况下用递归方法实 现回溯算法。 void backtrack( int t ) { if( t>n ) output( x ); //t>n时,算法已经搜索到叶节点, n控制递归深度 else for( int i = f( n, t ); i <= g( n, t ); i++ ) { // f()和g()分别表示当前扩展点处未处理过的子树的 //起始与终止编号 x[ t ] = h( i ); //h()表示当前扩展点处x[t]的第i个可选值 if( constraint( t ) && bound( t )) backtrack( t+1 ); //constraint()是当前扩展结点处的约束函数; //bound()是当前扩展结点处的限界函数 } }
24
for (int i=0; i<=n; i++) { X.m[i][1] = M[i][1]; X.m[i][2] = M[i][2]; X.x[i] = i; X.f2[i] = 0; } X.Backtrack(1); delete [ ] X.x; delete [ ] X.f2; return X.bestf;
批处理作业调度
解空间:排列树 void Flowshop::Backtrack(int i) { //当前结点位于排列树的第i-1层 if (i>n) //构造最优解 { bestf = f; //最优值 for (int j=1; j<=n; j++) bestx[j]=x[j]; //最优作业调度 return; }
11
装载问题
将第一艘轮船尽可能装满等价于选取全体集装箱的 一个子集,使该子集中集装箱重量之和最接近c1。 装载问题等价于以下特殊的0-1背包问题。 n
max wi xi
i 1 n
s.t. wi xi c1
i 1
xi {0,1},1 i n
用回溯法设计解装载问题的O(2n)计算时间算法。在 某些情况下该算法优于动态规划算法。
12
装载问题
解空间:子集树 n 可行性约束函数(选择当前元素): wi xi c1
i 1
上界函数(不选择当前元素): 当前载重量cw+剩余集装箱的重量r≤当前最优 装载重量bestw
13
装载问题
普通回溯 void backtrack (int i) { // 搜索第i层结点 if (i > n) { // 到达叶结点 更新最优解值bestw (当前最优载重量) ; return;} if (cw + w[i] <= c) { // 搜索左子树 cw += w[i]; //x[i]=1, 装入该集装箱,进入左子树 backtrack(i + 1); cw -= w[i]; //不装入该集装箱,为进入右子树作准备 } backtrack(i + 1); //进入右子树 }
n=3时的0-1背包问题用完全二叉树表示的解空间
W=[16,15,15] P=[45,25,25]
3
问题的解空间
n=3时的0-1背包问题用完 全二叉树表示的解空间
C=30 W=[16,15,15] P=[45,25,25]
4
回溯法
回溯法在问题的解空间树中,按深度优先策略,从根结点 出发搜索解空间树。 算法搜索至解空间树的任意一点(称为活结点)时,先判断 该结点是否包含问题的解。 如果肯定不包含,则跳过对该结点(称为死结点)为根的子 树的搜索,逐层向其祖先结点回溯; 否则,进入该子树(递归) ,继续按深度优先策略搜索。
由于w[i] 没有装入
15
装载问题
构造最优解 void backtrack (int i) { // 搜索第i层结点 if (i > n) // 到达叶结点 更新最优值bestx, bestw; return; r -= w[i]; if (cw + w[i] <= c) { // 搜索左子树 x[i] = 1; //最优解 cw += w[i]; backtrack(i + 1); cw -= w[i]; } if (cw + r > bestw) { x[i] = 0; // 搜索右子树 backtrack(i + 1); } r += w[i]; }
16
装载问题
装载类的设计: template <class Type> class Loading{ friend Type MaxLoading(Type[ ],Type,int); private: void Backtrack(int i); int n; //集装箱数 Type *w, //集装箱数组 c, //第一艘轮船的载重量 cw, //当前载重量 bestw; //当前最优载重量 };
批处理作业调度
给定n个作业的集合{J1,J2, …, Jn}。每个作业必须先 由机器1处理,然后由机器2处理。 作业Ji需要机器j的处理时间为tji。对于一个确定的作 业调度,设Fji是作业i在机器j上完成处理的时间。 所有作业在机器2上完成处理的时间和称为该作业调 度的完成时间和。 n f F2i
4 (next page) 22
批处理作业调度
解空间:排列树 for (int j=i; j<=n; j++) //当前结点的所有孩子 { f1 += m[x[j]][1]; //机器1的时间 f2[i] = ((f2[i-1]>f1) ? f2[i-1] : f1)+m[x[j]][2]; //机器2的时间 f += f2[i+1]; //当前的总时间 if (f<bestf) //不超过当前的最优值时 { swap(x[i], x[j]); //x[j]作为解空间的结点 Backtrack(i+1); swap(x[i], x[j]); //恢复x[j] } f1 -= m[x[j]][1]; //因为没有递归,恢复f1, f f -= f2[i]; 4 } } 23
14
装载问题
回溯(使用上界函数) void backtrack (int i) {// 搜索第i层结点 if (i > n) // 到达叶结点, 更新最优值bestw bestw=cw; return; r -= w[i]; //r-剩余集装箱的重量 if (cw + w[i] <= c) { // x[i]=1, 搜索左子树 cw += w[i]; backtrack(i + 1); cw -= w[i]; } if (cw + r > bestw) { // x[i]=0, 搜索右子树 backtrack(i + 1); } r += w[i]; }
相关文档
最新文档