第 5 章 回溯法 ..5.1概述 在现实世界中,很多问题没有(至少目前没有)有效的算法,这些问题的求 解只能通过蛮力穷举搜索来实现。但蛮力法需要生成并评估所有的解,执行效 率低下。 回溯法(BackTrackingMethod)是一种深度优先搜索算法,该方法试图穷 举所有可能的解,因此,回溯法能够确保获得最优解。在搜索过程中根据问题 约束等规则对搜索树进行剪枝操作,跳过部分搜索区域,提高算法性能。回溯 法按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并 不优或达不到目标,就退回一步重新选择,这种走不通就退回再选择另外一个 方向试探的技术即为回溯法,满足回溯条件的某个状态的点称为“回溯点”。回 溯法把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策 略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。 ..5.回溯法设计思路 2 复杂问题常常有很多可能解,这些可能解构成问题的解空间,也就是在穷 举法中提到的所有可能解的搜索空间。一般而言,解空间中应该包括所有的可 能解。 回溯法按深度优先策略搜索问题的解空间树。首先从根结点出发搜索解 空间树,当算法搜索至解空间树的某一结点时,先利用剪枝函数判断该结点是 否可行(即能得到问题的解)。如果不可行,则跳过对该结点为根的子树的搜 索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。 ..5.回溯法示例与过程分析 3 5.1 n 皇后问题 3. 例5.1 在n×n 格的国际象棋棋盘上摆放 n 个皇后( 1, 8), 见图5.此时n= 82算法设计与问题求解(微课版) 图5.1八皇后问题示意图 使其不能互相攻击,即任意两个皇后都不能处于 同一行、同一列或同一斜线上,有多少种摆法? n皇后是由八皇后问题演变而来的。该问题 由国际象棋棋手马克斯·贝瑟尔于1848 年提出: 在8×8 格的国际象棋棋盘上摆放8个皇后,使其 不能互相攻击,即任意两个皇后都不能处于同一 行、同一列或同一斜线上,求有多少种摆法。高斯 认为有76 种方案。1854 年在柏林的象棋杂志上 不同的作者发表了40 种不同的解,后来有人用图 论的方法解出92 种结果。 下面用数组模拟棋盘,从第一行开始,依次选 择位置,如果当前位置满足条件,则向下选位置, 如果不满足条件,那么当前位置后移一位。以四皇后为例,从第一行开始,依次选择位置, 如果当前位置满足条件,则向下选位置,如果不满足条件,那么返回上一步。 如图5.2(a)所示,第一个皇后放置于第一行第一列(1-1), 则第二个皇后可放置于 (2-3), 此时第三个皇后所有位置均不可放置,返回上一步。 图5.四皇后问题回溯模拟图 2 第5章 回溯法 83 如图5.2(b)所示,第二个皇后放置于(2-4),则第三个皇后可放置于(3-2),此时第四 个皇后所有位置均不可放置,返回上一步。 如图5.2(c)所示,第一个皇后放置于(1-2),则第二、三、四个皇后可顺利放置于 (2-4)、(3-1)、(4-3)中,得到一个可行解。 同理,如图5.2(d)所示,第一个皇后放置于(1-3),则第二、三、四个皇后可顺利放置于 (2-1)、(3-4)、(4-2)中,得到第二个可行解。 值得注意的是,问题的求解并不需要用n×n 的数组来表示结果,而只需要一个n 长 度的数组来表示每一行的皇后位置即可,即arr[i]=k,表示第i 行的皇后位置为k。此 时使用一个arr[n]的数组就可以表示一个解,通过回溯可以使我们得到所有可行解。 n 皇后问题的回溯法算法如下所示。 //输入: n,表示皇后个数 //输出: n 皇后问题的所有可行解 1. 初始化解向量arr[i]={-1} 2. i=1 3. while(i>=1) 3.1 把皇后i 摆放在下一列,即arr[i]++ 3.2 从arr[i]位置开始依次考察每一列,如果皇后i 摆放在arr[i]位置不发生冲突,则转3.3 步骤;否则arr[i]++试探下一列 3.3 若n 个皇后已全部摆放,则输出一个解,算法结束 3.4 若尚有皇后未摆放,则转步骤3 摆放下一个皇后 3.5 若arr[i]出界,则回溯,即arr[i]=-1,i--,转步骤3 重新摆放皇后i 4. 退出循环,说明n 皇后问题无解 0-1背包问题 5.3.2 0-1背包问题 例5.2 有一个背包,最多能承载10kg,现在有3个物品,质量分别为[4,8,5]、价值 分别为[24,40,20],详情如表5.1所示。那么应该如何选择才能使得你的背包背走最多 价值的物品? 表5.1 0-1背包问题示例 物品质量(w)/kg 价值(v)/元价值/质量(v/w) 1 4 24 6 2 8 40 5 3 5 20 4 0-1背包问题属于最优解问题,用回溯法需要构造解的子集树。对于每一个物品i, 该物品只有选与不选2个决策,总共有n 个物品,可以顺序依次考虑每个物品,这样就形 成了一棵解空间树。求解的基本思想就是遍历这棵树,以枚举所有情况,最后进行判断, 如果质量不超过背包容量,且价值最大的话,该方案就是最后的答案。 84 算法设计与问题求解(微课版) 0-1背包问题的回溯法解空间树如图5.3所示。 图5.3 0-1背包问题的回溯法解空间树 0-1背包问题的回溯法求解过程如下。 (1)选择物品1,此时背包的质量为4,价值为24。 (2)选择物品2,此时背包的质量为12,超出背包容量,因此物品2不可选择,回溯至 结点2。 (3)不选择物品2,行至结点6;选择物品3,行至结点7,此时背包的质量为9,价值为 44,由于已达叶子结点,则得到可行解(1,0,1),即该解选择物品1与3。 (4)回溯至结点6,不选择物品3,则得到可行解(1,0,0),即该解选择物品1,总价值 为24,小于先前最优解44。 (5)回溯至结点1,不选择物品1,行至结点9。 (6)选择物品2,此时背包的质量为8,价值为40,行至结点10。 (7)选择物品3,行至结点11,超出背包容量,回溯至结点10。 (8)不选择物品3,行至结点12,则得到可行解(0,1,0),即该解选择物品2,总价值为 40,小于先前最优解44。 (9)回溯至结点9,不选择物品2,行至结点13。 (10)选择物品3,此时背包的质量为5,价值为20,行至结点14,则得到可行解(0,0,1), 即该解选择物品3,总价值为20,小于先前最优解44。 (11)回溯至结点13,不选择物品3,行至结点15,则得到可行解(0,0,0),即该解不选 择任何物品,总价值为0,小于先前最优解44。 (12)解空间树遍历完毕,输出最优解(1,0,1),总价值为44。 0-1背包问题的回溯法算法如下所示。 //输入: 背包的最大容量C,物品个数n,每一个物品的质量wi,价值vi //输出: 问题的最优解选择的物品x0[],总价值max rv=0;rw=0; //未考虑的物品总价值与总质量初始化 knapsack(k,r,cv,rc,rw) //k 为解空间树的层数,r 为背包当前剩余容量,cv 为当前结 //点已装入背包物品的总价值 第5章 回溯法 85 if r>=0 and cv>max then //找到并更新最优解 max = cv; x0[]= x[1..k]; x0[k+1..n]= 0; end if if rw<=r then //剪枝 if cv+rv>max then max=cv+rv; x0[]= x[1..k]; x0[k+1..n]= 0; end if else if r>0 and cv+rv>max then rv=rv-v[k+1]; rw=rw-w[k+1]; x[k+1]=0; knapsack(k+1,r,cv,rc,rw); //搜索左子树 x[k+1]=1; knapsack(k+1,r-w[k+1],cv+v[k+1];,rc,rw); //搜索右子树 end if end if end knapsack 图的m 着 色问题 5.3.3 图的m 着色问题 例5.3 给定无向连通图G=(V,E)和正整数m ,求最小的整数m ,使得用m 种颜色 对G 中的顶点着色后任意两个相邻顶点着色不同。 用m 种颜色为无向图G=(V,E)着色,其中,V 的顶点个数为n,可以用一个n 元组 C=(c1,c2,…,cn)来描述图的一种可能着色,其中,ci∈{1,2,…,m }(1≤i≤n)表示赋予 顶点i 的颜色。 如图5.4(a)所示,有A、B、C、D、E共5个顶点组成的无向图G,如果采用图5.4(b)的 着色方法,可以看到相邻顶点B、C着色相同,因此该解为错误解;如果采用图5.4(c)的着 色方法,则满足题目条件,因此该解为可行解。 图5.4 图的m 着色问题示例 以图5.4(a)为例,图的m 着色问题的解空间树搜索过程如下。 (1)A=1,即顶点A 着色1。 (2)B=1,即顶点B着色1,而A、B两顶点相邻,不满足题意,因此进行回溯。 86 算法设计与问题求解(微课版) (3)B=2,即顶点B着色2。 (4)C=1,即顶点C着色1,而A、C两顶点相邻,不满足题意,因此进行回溯。 (5)C=2,即顶点C着色2,而B、C两顶点相邻,不满足题意,因此进行回溯。 (6)C=3,即顶点C着色3。 (7)D=1,即顶点D着色1。 图5.5 图的m 着色问题解 空间树搜索过程 (8)E=1,即顶点E着色1,而D、E两顶点相邻,不满足 题意,因此进行回溯。 (9)E=2,即顶点E着色2,而B、E两顶点相邻,不满足 题意,因此进行回溯。 (10)E=3,即顶点E着色3,而C、E两顶点相邻,不满足 题意,因此进行回溯。 (11)E=3,即顶点E着色3,而C、E两顶点相邻,不满足 题意,因此进行回溯。 (12)D=2,即顶点D着色2,而B、D两顶点相邻,不满足 题意,因此进行回溯。 (13)D=3,即顶点D着色3。 (14)E=1,即顶点D着色1,此时所有顶点均完成着色, 且满足题意,得到可行解5元组(1,2,3,3,1)。 图的m 着色问题的解空间树搜索过程如图5.5所示。 图的m 着色问题的回溯法算法如下所示。 //输入: 无向图点与边之间的关系(邻接矩阵),m 种颜色 //输出: 无向图各顶点的着色记录color[n] 1 将数组color[n]初始化为0 2 k=0; //第一个顶点 3 while(k>=0) 3.1 依次考察每一种颜色,若顶点k 的着色与其他顶点的着色不发生冲突,则转步骤3.2;否则, 搜索下一个颜色 3.2 若顶点已全部着色,则输出数组color[n],返回 3.3 否则 3.3.1 若顶点k 是合法着色,则k=k+1,转步骤3 处理下一顶点 3.3.2 否则,重置顶点k 的着色情况,k=k-1,转步骤3 回溯 5.3.4 批处理作业调度问题 例5.4 设有n 个作业{1,2,…,n}要在两台机器上处理,每个作业要先在机器1上 处理,再在机器2上处理,请给出一种作业调度方案,使得所有任务完成的总时间最短。 对于这个问题,不难想到一个最优调度应使机器1没有空闲时间,且机器2的空闲时 间最小。 我们给出一个实例,现有3个作业{1,2,3},在机器1上所需的处理时间为(2,3,2), 第5章回溯法87 在机器2上所需的处理时间为(1,1,3) 。如果以图5.6的调度方案,即作业处理顺序为 1、2、3,则完成时间为10;如果以图5.7的调度方案,即作业处理顺序为1、3、2,则完成时 间为8。调度方案与处理过程描述如下。 图5.6批处理作业调度方案一 图5.7批处理作业调度方案二 1.调度方案一 (1)机器1处理作业1,时间为0~2,此时机器2空闲。 (2)机器1处理作业2,时间为2~5;机器2处理作业1,时间为2~3,到时间3时,机 器2空闲。 (3)机器1处理作业3,时间为5~7;机器2处理作业2,时间为5~6,到时间7时,机 器2空闲。 (4)机器2作业3,时间为7~10,完成全部作业。 2. 调度方案二 (1)机器1处理作业1,时间为0~2,此时机器2空闲。 (2)机器1处理作业3,时间为2~4;机器2处理作业1,时间为2~3,到时间3时,机 器2空闲。 (3)机器1处理作业2,时间为4~7;机器2处理作业3,时间为4~7,机器2无空闲。 (4)机器2处理作业2,时间为7~8,完成全部作业。 由上述两种调度方案可以看出,方案一中机器2的总空闲时间为5,方案二中机器2 的总空闲时间为3,因此方案二比方案一节省了时间2,验证了我们之前的设想。 为了求解该问题,我们进行如下假设。 x[:表示 n 个作业批处理的调度方案。 n] k]: 表示第 k 个作业的编号 。 x[ n]:机器1当前完成时间 。 sum1[ sum2[:机器2当前完成时间 。 n] sum1[:安排第 k 个作业后 , k]机器1完成时间。 88 算法设计与问题求解(微课版) sum2[k]:安排第k 个作业后,机器2完成时间。 则: sum1[k]=sum1[k-1]+作业x[k]在机器1的处理时间。 sum2[k]=max{sum1[k],sum2[k-1]}+作业x[k]在机器2的处理时间。 批处理作业调度问题的回溯算法可表示如下。 //输入: n 个作业在机器1 上的处理时间a[n],在机器2 上的处理时间b[n] //输出: 最优调度序列x[n] 1.初始化解向量x[n]={-1};最短完成时间bestTime=MAX; 2.初始化调度方案中机器1 和机器2 的完成时间 sum1[n]=sum2[n]={0}; k=1 3. while(k>=1) 3.1 依次考察每一个作业,如果作业x[k]尚未处理,则转步骤3.2,否则尝试下一个作业,即 x[k]++ 3.2 处理作业x[k]: 3.2.1 sum1[k]=sum1[k]+a[x[k]] 3.2.2 sum2[k]=max{sum1[k],sum2[k-1]}+b[x[k]] 3.2.3 若sum2[k]<bestTime,则转步骤3.3,否则实施剪枝 3.3 若n 个作业已全部处理,则输出一个解; 3.4 若尚有作业没被处理,则k++,转步骤3 处理下一个作业 3.5 回溯,x[k]= -1, k--, 转步骤3 重新处理第k 个作业 .. 5.4 能力拓展 5.4.1 全排列问题 例5.5 规定n 的全排列是自然数1到n 的所有不重复排列。现在给出n,要求输出 n 的全排列。 问题分析:首先考虑规模较小情况,当n=3时,全排列为{123,132,213,231,312, 321}。由于问题要求输出所有不重复的情况,因此很容易就想到可以采用枚举的方法输 出所有的情况。 题目明确要求将n 个数放置在n 个位置上,所以可以枚举n 个位置,在每个位置上 填入n 个数放到当前位置上,同时还需要保证不跟之前已填入的数重复,所以在求解过 程中还要记录前面已经填入的数来确保没有重复情况。当将n 个数放完之后,就可以输 出一个结果,然后向上回溯遍历其他情况。 以n=3为例,回溯法的解决思路如下(见图5.8)。 (1)在位置1中填入数字1,位置2中填入数字2,位置3中填入数字3,此时已填入n 个数字,到达叶子结点,可输出结果123。 (2)回溯至位置2,在位置2中填入数字3,位置3中填入数字2,此时已填入n 个数 字,可输出结果132。 第5章回溯法89 图5.8回溯法求解全排列问题示意图 (3)回溯至位置1,在位置1中填入数字2,位置2中填入数字1,位置3中填入数字 3,此时已填入n个数字,可输出结果213 。 (4)回溯至位置2,在位置2中填入数字3,位置3中填入数字1,此时已填入n个数 字,可输出结果231 。 (5)回溯至位置1,在位置1中填入数字3,位置2中填入数字1,位置3中填入数字 2,此时已填入n个数字,可输出结果312 。 (6)回溯至位置2,在位置2中填入数字2,位置3中填入数字1,此时已填入n个数 字,可输出结果321 。 4.存在障碍物的迷宫问题 5.2 例5.给定一个n×m 的迷宫,迷宫中有些格子存在障碍物,无法移动到这个格子 6 里面,其余格子均为空。求从起点到终点至少需要多少步。规定每次移动只能水平或垂 直移动。 输入描述:第一行输入两个整数n、m,表示迷宫的行数和列数。 接下来 n 行,每行输入 m 个字符,表示整个迷宫。 空的格子用“.表(”) 示,有障碍物的格子用#表示。 起点用S表示,终点用T表示。 首先利用图5. 9所示问题来进行分析。位置S表示迷宫起 点,位置T表示迷宫终点,#表示该位置有障碍物,“.表(”) 示该位 置可到达。 由于起点和终点位置未知,因此需要先遍历找出起点S和 终点T的位置。根据题目要求,我们只能水平或垂直移动,所以 在移动的过程中只有上、下、左、右4个方向。每次移动时枚举 这4种情况,并判断是否会出界。因为要求找到从起点S到终 点T的最短距离,所以还需要对当前所在位置的步数进行标记, 9 每次移动完毕后判断此时走到该点的步数是否小于上次途径该 图5.存在障碍物的 迷宫问题示例 点的频数,不小于则向上回溯,小于则继续向下走。同时,当走 90算法设计与问题求解(微课版) 到一个无法再移动的点时也需要向上回溯(表明迷宫中此处不通) 。如果移动至终点,则 更新最短路径结果,并向上回溯其他的情况。 以图5.9为例,回溯法的求解思路如下。 (1)遍历整个二维数组(迷宫中所有位置), 找出起点S和终点T。 (2)从起点S(1,1)出发,假设以上左下右的优先方式进行移动,结合不能越界、不能 通过障碍物的约束条件,向下依次经过(2,1) 、(3,1) 、(4,1) 、(5,1), 无法再移动,回溯至 (4,1), 同样无法再移动,回溯至(3,1) 。 (3)从位置(3,1)向右移动至(3,2) 、(3,3), 并依次向上移动至(2,3), 向右移动至 (2,4), 向上移动至(1,4), 此时无法再移动,回溯至(2,4), 并向下通过(3,4) 、再向下到达 终点(4,4), 此时路径长度结果为8。 (4)回溯至位置(3,3), 以上左下右的优先方式依次经过(4,3) 、(5,3) 、(5,4)并到达 终点(4,4), 此时路径长度结果为8。 (5)回溯至位置(4,3), 并直接向右到达终点(4,4), 此时路径长度结果为6,更新最短 路径结果。 (6)再次回溯至位置(3,3), 并经过(3,4)到达终点(4,4), 此时路径长度结果也为6。 5.4.3图的m着色问题变种 例5.7我们规定字母表示一个电台,数字则表示广播电台的无线频谱。如果相邻两 个电台之间运用同样的无线频谱就会互相干扰。所以只有所有相邻电台运用的都是不同 的无线频谱,所有电台才能都正常运作。最少需要多少个无线频谱才能让所有电台都正 常运作? 本题跟图的 m 着色问题类似,但是该题要求的是至少需要多少种颜色才能让每个相 邻点之间颜色不相同(最少需要多少种无线频谱才能 使相邻的电台互不干扰)。 以图5.电台A与电台B、电台B 10 为例,C相邻, 与电台A、C、D相邻,电台C与电台A、B、D相邻,电台 D与电台B、C相邻。 回溯法的求解思路如下。图5.10 图的 m 着色问题变种示例 (1)对A点(电台A)进行操作,因为至少需要一 种无线频谱,所以sum=1,此时A点相邻的B、C两点均未给定无线频谱,因此遍历B点。 (2)将无线频谱1给予B点,发现存在A点与它相邻并且无线频谱相同。所以B点 不能使用无线频谱1,我们需要加入一个新的无线频谱才能让B点拥有无线频谱并且满 足条件。因此,给B点无线频谱2,此时其他相邻点的无线频谱均与其不同。 sum++, (3)同上将无线频谱1给予C点,发现存在相邻点A与之干扰,将无线频谱2给予C 点,同时发现存在相邻点B与之干扰,所以加入一个新的无线频谱3给予C点,sum++, 此时其他相邻点的无线频谱均与其不同。 (4)此时我们发现对于D点来说,使用无线频谱1即可满足所有约束条件。 (5)完成所有电台的无线频谱安排,输出结果“最少需要3种无线频谱”。