算法设计第六章答案
解空间:深度为 n 完全 m 叉树 两个部件,三个供应商
A
1
2
3
B 1 23
C 1 23
EF G
HI J
D 1 23
KL M
根据条件可知:剪枝函数,约束函数是总价格不超过 d,界限函数是当前扩展结 点的总质量最小值大于当前最优总重量; 优先队列式分支限界法:优先级定义为结点已知部分的总重量;因而,每一次选 择新的扩展结点都是选择已知部分总重量最小的结点,因而界限函数可以省略; 算法流程: 主函数是一个 while 循环,结束条件是当前扩展结点的深度是 n,因为是优先队 列式分支限界(每一次都选择当前最小重量的结点作为扩展结点),因而第一个 找到的叶结点肯定是最优解; 每一次循环所需要完成的工作包括:1)将扩展结点的所有儿子结点加至活结点 列表中;其中,需要利用约束函数进行剪枝;2)按照“先进先出”原则,选择 下一个扩展结点。
活结点列表
是
为空?
否
是
当前扩展结点 在排列树中的深度
是n-1?
否
将当前扩展结点的儿子结点 分别加至活结点列表
输出最优解 结束
是
所有儿子结点
都已经得到判断?
否
选择下一个儿子结点,并且计 算各连接块的最大长度
各连接块的 当前最大长度小于当前
否
最优长度?
是
将当前儿子结点加至 活结点列表
将儿子结点加至活 结点列表
开始 根结点加入活结点列表,
并作为当前扩展结点
活结点列表
是
为空?
否
将当前扩展结点的儿子结点 分别加至活结点列表
左儿子结点
否
满足容量约束?
是
将左儿子结点加至 活结点列表
右儿子结点的 最大价值大于当前
否
最优价值?
是
将右儿子结点加至 活结点列表
按照“后进先出”,选择下 一个扩展结点
输出最优解 结束
将儿子结点加至活 结点列表
if(ok(r,c,board[r][i])) { Swap(board[r][c],board[r][i]); if(c==n) { if(r==m) count+=1.0; else Backtrace(r+1,2); } else Backtrace(r,c+1); Swap(board[r][c],board[r][i]); } }
解空间:对于每一行而言,是一个深度为 n 的排列树;因而,整个空间是 m 个 深度为 n 的排列树。 剪枝函数:每一列上的 m 个元素存在重复。 因而,这个问题存在两个有关深度的变量 r(行)和 c(列),即 Backtrace(r,c)
void Backtrace(int r,int c) { for(int i=c;i<=n;i++)
按照“先进先出”原则,选 择下一个扩展结点
开始
根结点加入活结点列表, 并作为当前扩展结点
当前扩展结点在解 是 空间数的深度是n?
否 将当前扩展结点的儿子结点
分别加至活结点列表
是
所有儿子结点
都已经得到判断?
否
选择下一个儿子结点,并且计算 当前儿子结点的总重量和总价格
当前儿子结点的
否
当前总价格小于d?
算法流程: 主函数是一个 while 循环,结束条件是活结点列表为空,表示所有叶结点都已经 遍历完成;此时,应该输出整个问题的最优解。 每一次循环需要完成的工作包括:1)如果当前扩展结点的深度是 n-1计算每一个连接块的长度,并且计算所有连接块的 最大长度;2)如果当前深度小于 n-1,则需要将扩展结点的儿子结点加至活结点 列表;其中,需要利用界限函数进行剪枝(界限函数就是各连接块的当前最大长 度小于当前最优长度);3)按照“先进先出”原则,选择下一个扩展结点。
设计一个解 01 背包问题的栈式分支限界法,并说明栈式分支限界法与回溯法的 区别。
解空间:子集树
A
1
0
B
1
0
C
1
0
D
E
F
G
1)队列式分支界限法和栈式分支界限法对比: 队列式分支限界法活结点列表变化过程:(先进先出)
1{A} 2{B,C} 3{C,D,E} 4{D,E,F,G} 5{E,F,G} 6{F,G} 7{G} 8{}
1
B
2
3
E
F
3
2
K
L
A 2
C
1
3
3
D
1
2
G
H
I
J
3
1
2
1
M
NO
P
注意:与课本例题最小密度电路板排列问题不同,在每一次将当前扩展结点的儿 子结点加至活结点列表时(当深度加 1 时),应该更新和计算儿子结点的当前最 小长度; 对于每一个结点而言,需要维护两个数组,low[1:m]和 high[1:m],分别记录着每 一个连接块的当前已知的起点插槽序号和终点插槽序号,当深度增加 1 时,首先 判断连接块 Nj 是否需要跨越 x[i+1]所对应的电路板; 假设 x[1:i]已知,x[i+1]是当前儿子结点所对应的第 i+1 层元素取值,则数组 low 和 high 更新方法是: 1) 如果连接块 Nj 跨越 x[i+1]电路板,而且当前 low[j]大于 i+1,则将 low[j]更新
算法实现题 6-4: 最小重量机器设计问题 问题描述:设某一个机器由 n 个部件组成,每一种部件都可以从 m 个不同的供 应商处购得。设 wij 是从供应商 j 处购得的部件 i 的重量,cij 是相应的价格。 设计一个优先队列式分支限界法,给出总价格不超过 d 的最小重量机器设计。 编程任务: 对于给定的机器部件重量和机器部件价格,设计一个优先队列式分支限界法,计 算总价格不超过 d 的最小机器设计。
是
将当前儿子结点加至 活结点列表
输出最优解 结束
将儿子结点加至活 结点列表
为 i+1; 2) 如果连接块 Nj 跨越 x[i+1]电路板,而且当前 high[j]小于 i+1,则将 high[j]更
新为 i+1; 3) 其余情况,则不需要更新 low[j]和 high[j]。
开始
根结点加入活结点列表, 并作为当前扩展结点
按照“先进先出”,选择下 一个扩展结点
计算各连接块的最小长度, 并且更新当前最优长度
2)程序流程 整个流程与队列式分支限界法的 01 背包问题一样,只是插入活结点的操作:由 insert()改为 push(),重新选择下一个扩展结点的操作:由 delete()改为 pop()。 算法主函数是: 一个 while(1)循环,循环结束条件是活结点列表为空,即当前扩展结点为空,表 示所有可行的结点都已经得到遍历;并且输出最优解。 每一次循环作用是:在当前的扩展结点下,将以当前扩展结点作为父结点的下一 层子结点都加入到活结点列表里,并且选择一个新的扩展结点,进入下一层循环; 其中,剪枝函数包括左结点的容量约束(约束函数)和右结点的最大价值判断(界 限函数);
算法实现题 6-1:最小长度电路板排列问题 问题描述:最小长度电路板排列问题是大规模电子系统设计中提出的实际问题。 该问题的提法是,将 n 块电路板以最佳排列方案插入带有 n 个插槽的机箱中。n 块电路板的不同排列方式对应与不同的电路板插入方式。 设 B={1,2,···,n}是 n 块电路板的集合。集合 L={N1,N2,···,Nm}是 n 块电 路板的 m 个连接块。其中,每个连接块 Ni 是 B 的一个子集,且 Ni 中的电路板 用同一根导线连接在一起。 在最小长度电路板排列问题中,连接块的长度是指该连接块中第一块电路板到最 后一块电路板之间的距离; 试设计一个队列式分支界限法找出所给 n 个电路板的最佳排列,使得 m 个连接 块中最大长度达到最小。 解空间:排列树
算法实现题 5-9:拉丁矩阵问题 问题描述:现有 n 种不同形状的宝石,每种宝石有足够多颗。欲将这些宝石排列 成 m 行 n 列的一个矩阵,m<=n,使矩阵中每一行和每一列的宝石都没有相同形 状。试设计一个算法,计算出对于给定的 m 和 n,有多少中不同的宝石排列方案。 即,给定一个 m*n 大小的矩阵,每一行 n 个元素是 1~n 的一种排列, 拉丁矩阵要求每一列上的 m 个元素不能够有重复。
栈式分支限界法活结点列表变化过程:(后进先出)
1{A} 2{B,C} 3{B,F,G} 4{B,F} 5{B} 6{D,E} 7{D} 8{} 从各结点的扩展次序来讲,队列式分支限界法是标准的宽度优先,栈式分支限界 则类似于深度优先。 回溯法: 各结点的遍历次序是:
A->B->D->B->E->A->C->F->C->G 回溯法是深度优先,而且结点 A,B,C 都两次成为扩展结点; 但是,栈式分支限界法各结点只有一次机会成为扩展结点。
其中,第一个 for 循环:同一深度上的各子结点扩展;ok 函数是剪枝函数; 如果不考虑“行 r”,并且去掉红色方框中的代码,则整个回溯函数跟排列树形式 的代码框架一样;其中,红色部分主要是针对“行 r”进行递归(深度加 1)。
习题 6.1:01 背包问题的栈式分支限界法 栈式分支限界法将活结点表以后进先出(LIFO)的方式存储于一个栈中。试