5.1单项选择题及其参考答案 5.1.1单项选择题 1. 回溯法是在问题的解空间中按策略从根结点出发搜索的。 A. 广度优先B. 活结点优先 C. 扩展结点优先D. 深度优先 2. 下列算法中通常以深度优先方式搜索问题的解。 A. 回溯法B. 动态规划C. 贪心法D. 分支限界法 3. 关于回溯法,以下叙述中不正确的是。 A. 回溯法有通用解题法之称,可以系统地搜索一个问题的所有解或任意解 B. 回溯法是一种既带系统性又带有跳跃性的搜索算法 C. 回溯算法需要借助队列来保存从根结点到当前扩展结点的路径 D. 回溯算法在生成解空间的任一结点时,先判断该结点是否可能包含问题的解,如 果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向祖先结点回溯 4. 回溯法的效率不依赖于下列因素。 A. 确定解空间的时间B. 满足显式约束的值的个数 C. 计算约束函数的时间D. 计算限界函数的时间 5. 下面是回溯法中为避免无效搜索采取的策略。 A. 递归函数B. 剪枝函数C. 随机数函数D. 搜索函数 6.对于含有n个元素的子集树问题(每个元素二选一), 最坏情况下解空间树的叶子结 点个数是。 A.n! B.2nC.2n+1-1 D.2n-1 7. 用回溯法求解0/1背包问题时的解空间是。 A. 子集树B. 排列树 C. 深度优先生成树D. 广度优先生成树 8. 用回溯法求解0/1背包问题时的最坏时间复杂度是。 A.O(n) B.O(nlog2n) C.O(n×2n) D.O(n2) 9. 用回溯法求解TSP 问题时的解空间是。 A. 子集树B. 排列树 C. 深度优先生成树D. 广度优先生成树 10. 有 n 个学生,每个人有一个分数,求最高分的学生的姓名,最简单的方法 是。 A. 回溯法B. 归纳法C. 迭代法D. 以上都不对 11. 求中国象棋中马从一个位置到另外一个位置的所有走法,采用回溯法求解时对应 的解空间是。 A. 子集树B. 排列树 106 C. 深度优先生成树D. 广度优先生成树 12.n个人排队在一台机器上做某个任务,每个人的等待时间不同,完成他的任务的时 间不同,求完成这n个任务的最小时间,采用回溯法求解时对应的解空间是。 A. 子集树B. 排列树 C. 深度优先生成树D. 广度优先生成树 5.1.2单项选择题参考答案 1. 答:回溯法采用深度优先搜索在解空间中搜索问题的解。答案为D。 2. 答:回溯法采用深度优先搜索在解空间中搜索问题的解,分支限界法采用广度优先 搜索在解空间中搜索问题的解。答案为A。 3. 答:回溯算法是采用深度优先遍历的,需要借助栈保存从根结点到当前扩展结点的 路径。答案为C。 4. 答:回溯法的解空间是虚拟的,不必事先确定整个解空间。答案为A。 5. 答:剪支函数包括约束函数(在扩展结点处剪去不满足约束条件的路径)和限界函数 (剪去得不到问题的解或最优解的路径) 。答案为B。 6. 答:这样的解空间树是一棵高度为n+1 的满二叉树,叶子结点恰好有2n个。答案 为B。 7. 答:在0/1背包问题中每个物品是二选一(要么选中,要么不选中), 与物品的顺序无 关,对应的解空间为子集树类型。答案为A。 8. 答:0/1背包问题的解空间是一棵高度为n+1 的满二叉树,结点个数为2n+1-1,最 坏情况下搜索全部结点。答案为C。 9. 答:TSP 问题的解空间属于典型的排列树,因为路径与顶点的顺序有关。答案为B。 10. 答:最简单的方法是依次迭代比较求最高分数。答案为C。 11. 答:每一步马从相邻可走的位置中选择一个位置走下去。答案为A。 12. 答:该问题是求1~n的某个排列,对应n个任务完成的最小时间。答案为B。 5.2问答题及其参考答案 5.2.1问答题 1. 回溯法的搜索特点是什么? 2.有这样一个数学问题,x和y是两个正实数,求x+y=3的所有解,请问能否采用回 溯法求解? 如果 x 和 y 是两个均小于或等于10 的正整数,又能否采用回溯法求解? 如果 能够,请采用解空间画出求解结果。 对于n=4,7),31 的子集和问题,利用左、右剪支的回溯法算法求 解,求出所有解并且画出解空间中的搜索过程。 3.a=(11,13,24,t= 4.对于 n 皇后问题,通过解空间说明n=3时是无解的。 5.对于 n 皇后问题,有人认为当 n 为偶数时其解具有对称性,即 n 皇后问题的解个数 107 108 恰好为n/2皇后问题的解个数的两倍,这个结论正确吗? 6.请问能否采用解空间为排列树的回溯框架求解n 皇后问题? 如果能,请给出剪支操 图5.1 一个无向连通图 作,说明最坏情况下的时间复杂度,按照最坏情况下的时间复杂 度比较,哪个算法更好? 7.对于如图5.1所示的无向连通图,假设颜色数m =2,给 出m 着色的所有着色方案,并且画出对应的解空间。 8.有一个0/1背包问题,物品个数n=4,物品编号分别为 0~3,它们的重量分别是3、1、2和2,价值分别是9、2、8和6,背 包容量W =3。利用左、右剪支的回溯法算法求解,并且画出解 空间中的搜索过程。 9.以下算法用于求n 个不同元素a 的全排列,当a=(1,2,3)时,请给出算法输出的全 排列的顺序。 int cnt=0; //累计排列的个数 void disp(int a[]){ //输出一个解 System.out.printf(" 排列%2d: (",++cnt); for(int i=0;i=n-1) //递归出口 disp(a); else { for(int j=n-1;j>=i;j--) { swap(a,i,j); //交换a[i]与a[j] dfs(a,i+1); swap(a,i,j); //交换a[i]与a[j]:恢复 } } }v oid perm(int a[]) { //求a 的全排列 dfs(a,0); } 10.假设问题的解空间为(x0,x1,…,xn-1),每个xi 有m 种不同的取值,所有xi 取不 同的值,该问题既可以采用子集树递归回溯框架求解,也可以采用排列树递归回溯框架求 解,考虑最坏时间性能应该选择哪种方法? 11.以下两个算法都是采用排列树递归回溯框架求解任务分配问题,判断其正确性,如 果不正确,请指出其中的错误(其中,swap(x,i,j)用于交换x[i]和x[j])。 109 (1)算法1: void dfs(int x[],int cost,int i) { //回溯算法 if(i>n) { //到达叶子结点 if(costn) { //到达叶子结点 if(costO(n!),采用后者较好。 11.答:算法1是正确的。算法2不正确,在执行第一个swap(x[i],x[j])后已经为人 员i 分配了任务x[j],应该置task[x[i]]=true,cost+=c[i][x[i]],后面的回溯恢复过 程也是如此。 5.3 算法设计题及其参考答案 5.3.1 算法设计题 1.给定含n 个整数的序列a(其中可能包含负整数),设计一个算法从中选出若干整数, 使它们的和恰好为t。例如,a=(-1,2,4,3,1),t=5,求解结果是(2,3,1,-1)、(2,3)、 (2,4,-1)和(4,1)。 2.给定含n 个正整数的序列a,设计一个算法从中选出若干整数,使它们的和恰好为t 并且所选元素个数最少的一个解。 3.给定一个含n 个不同整数的数组a,设计一个算法求其中m (m ≤n)个元素的组合。 例如,a={1,2,3},m =2,输出结果是{{1,2},{1,3},{2,3}}。 4.设计一个算法求1~n 中m (m ≤n)个元素的排列,要求每个元素最多只能取一次。 例如,n=3,m =2时的输出结果是{{1,2},{1,3},{2,1},{2,3},{3,1},{3,2}}。 5.在求n 皇后问题的算法中每次放置第i 个皇后时,其列号xi 的试探范围是1~n,实 际上前面已经放好的皇后的列号是不必试探的,请根据这个信息设计一个更高效的求解n 皇后问题的算法。 6.请采用基于排列树的回溯框架设计求解n 皇后问题的算法。 7.一棵整数二叉树采用二叉链b 存储,设计一个算法求根结点到每个叶子结点的路径。 8.一棵整数二叉树采用二叉链b 存储,设计一个算法求根结点到叶子结点的路径中路 径和最小的一条路径,如果这样的路径有多条,求其中的任意一条。 9.一棵整数二叉树采用二叉链b 存储,设计一个算法产生每个叶子结点的编码。假设 从根结点到某个叶子结点a 有一条路径,从根结点开始,路径走左分支时用0表示,走右分 支时用1表示,这样的0/1序列就是a 的编码。 10.假设一个含n 个顶点(顶点编号为0~(n-1))的不带权图采用邻接矩阵A 存储, 设计一个算法判断其中顶点u 到顶点v 是否有路径。 113 11.假设一个含n 个顶点(顶点编号为0~(n-1))的不带权图采用邻接矩阵A 存储, 设计一个算法求其中顶点u 到顶点v 的所有路径。 12.假设一个含n 个顶点(顶点编号为0~(n-1))的带权图采用邻接矩阵A 存储,设 计一个算法求其中顶点u 到顶点v 的一条路径长度最短的路径。一条路径的长度是指路 径上经过的边的权值和。如果这样的路径有多条,求其中的任意一条。 13.给定一个m ×n 的迷宫,每个方格值为0时表示空白,为1时表示障碍物,在行走时 最多只能走到上、下、左、右相邻的方格。设计一个回溯算法求从指定入口s 到指定出口t 的所有迷宫路径和其中一条最短路径。 14.给定一个不带权连通图,由指定的起点前往指定的终点,途经所有其他顶点且只经 过一次,称为哈密顿路径,闭合的哈密顿路径称作哈密顿回路。设计一个算法求无向图的所 有哈密顿回路。 15.采用回溯法求解最优调度问题,假设有n 个任务由k 个可并行工作的机器来完成,完 成任务i(0≤i=n){ //到达一个叶子结点 if(cs==t){ //找到一个解 System.out.printf(" (%d): ",++sum); for(int j=0;j=n){ //到达一个叶子结点 if(cs==t){ //找到一个解 if(m=t){ //右孩子结点剪支 x[i]=0; //不选取整数a[i] dfs(cs,rs,x,m,i+1); } rs+=a[i]; //恢复剩余整数和 } }v oid subs(int a[],int t) { //求解子集和问题 this.a=a; n=a.length; this.t=t; int x[]=new int[n]; //解向量 int rs=0; //表示所有整数和