1、中国数学建模-编程交流- 贪婪算法feifei7 重登录 隐身 用户控制面板 搜索 风格 论坛状态 论坛展区 社区服务 社区休闲 网站首页 退出 VC+,C,Perl,Asp.编程学习, 算法介绍. 我的收件箱 (0) 中国数学建模 学术区 编程交流 贪婪算法 您是本帖的第 672 个阅读者 * 贴子主题:贪婪算法 b 等级:职业侠客 文章:472积分:951门派:黑客帝国 注册:2003-8-28第 11 楼 1.3.6 最小耗费生成树在例 1 - 2 及 1 - 3 中已考察过这个问题。因为具有 n 个顶点的无向网络 G 的每个生成树刚好具有 n-1 条边,所以问题是用某种方法选择 n-1
2、 条边使它们形成 G 的最小生成树。至少可以采用三种不同的贪婪策略来选择这 n-1 条边。这三种求解最小生成树的贪婪算法策略是: K r u s k a l 算法, P r i m 算法和 S o l l i n 算法。1. Kruskal 算法(1) 算法思想K r u s k a l 算法每次选择 n- 1 条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。K r u s k a l 算法分 e 步,其中 e 是网络中边的数目。按耗费递增的顺序来考虑这 e 条边,每次考虑一条边。当考虑某条边
3、时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。考察图 1-12a 中的网络。初始时没有任何边被选择。图 13-12b 显示了各节点的当前状态。边( 1 , 6)是最先选入的边,它被加入到欲构建的生成树中,得到图 1 3 - 1 2 c。下一步选择边( 3,4)并将其加入树中(如图 1 3 - 1 2 d 所示) 。然后考虑边( 2,7 ),将它加入树中并不会产生环路,于是便得到图 1 3 - 1 2 e。下一步考虑边( 2,3)并将其加入树中(如图 1 3 - 1 2 f 所示) 。在其余还未考虑的边中, (7,4)具有最小耗费,因此先考虑它,将它加入正在创建的树中会产
4、生环路,所以将其丢弃。此后将边( 5,4)加入树中,得到的树如图 13-12g 所示。下一步考虑边( 7,5 ) ,由于会产生环路,将其丢弃。最后考虑边( 6,5)并将其加入树中,产生了一棵生成树,其耗费为 9 9。图 1 - 1 3 给出了 K r u s k a l 算法的伪代码。/ /在一个具有 n 个顶点的网络中找到一棵最小生成树令 T 为所选边的集合,初始化 T=令 E 为网络中边的集合w h i l e(E ) 2004-5-27 19:42:37 b 等级:职业侠客 文章:472积分:951门派:黑客帝国 注册:2003-8-28第 12 楼 程序 13-6 Kruskal 算法
5、所需要的数据类型template class EdgeNode p u b l i c :operator T() const return weight;p r i v a t e :T weight;/边的高度int u, v;/边的端点 ;为了更简单地使用 8 . 1 0 . 2 节的查找和合并策略,定义了 U n i o n F i n d类,它的构造函数是程序 8 - 1 6 的初始化函数, U n i o n 是程序 8 - 1 6 的加权合并函数,F i n d 是程序 8 - 1 7 的路径压缩搜索函数。为了编写与网络描述无关的代码,还定义了一个新的类 U N e t Wo r
6、 k,它包含了应用于无向网络的所有函数。这个类与 U n d i r e c t e d 类的差别在于 U n d i r e c t e d 类中的函数不要求加权边,而 U N e t Wo r k 要求边必须带有权值。 U N e t Wo r k 中的成员需要利用 N e t w o r k 类中定义的诸如 B e g i n 和 N e x t Ve r t e x 的遍历函数。不过,新的遍历函数不仅需要返回下一个邻接的顶点,而且要返回到达这个顶点的边的权值。这些遍历函数以及有向和无向加权网络的其他函数一起构成了 W N e t w o r k 类(见程序 1 3 - 7) 。程序 1
7、3-7 WNetwork 类templateclass WNetwork : virtual public Networkpublic :virtual void First(int i, intvirtual void Next(int i, int ;象 B e g i n 和 N e x t Ve r t e x 一样,可在 A d j a c e n c y W D i g r a p h 及 L i n k e d W D i g r a p h 类中加入函数 F i r s t 与 N e x t。现在 A d j a c e n c y W D i g r a p h 及 L i
8、 n k e d W D i g r a p h 类都需要从 W N e t Wo r k 中派生而来。由于 A d j a c e n c y W G r a p h 类和 L i n k e d W G r a p h 类需要访问 U N e t w o r k 的成员,所以这两个类还必须从 U N e t Wo r k中派生而来。U N e t Wo r k : : K r u s k a l 的代码见程序 1 3 - 8,它要求将 Edges() 定义为 N e t Work 类的虚成员,并且把 U N e t Wo r k 定义为 E d g e N o d e 的友元) 。如果没有
9、生成树,函数返回 f a l s e,否则返回 t r u e。注意当返回 true 时,在数组 t 中返回最小耗费生成树。程序 13-8 Kr u s k a l 算法的 C + +代码templatebool UNetwork:Kruskal(EdgeNode t)/ 使用 K r u s k a l 算法寻找最小耗费生成树/ 如果不连通则返回 false/ 如果连通,则在 t 0 : n - 2 中返回最小生成树int n = Ve r t i c e s ( ) ;int e = Edges();/ /设置网络边的数组InitializePos(); / 图遍历器EdgeNode *E
10、 = new EdgeNode e+1;int k = 0; / E 的游标for (int i = 1; i H(1);H.Initialize(E, e, e);UnionFind U(n); / 合并/搜索结构/ 根据耗费的次序来抽取边k = 0; / 此时作为 t 的游标while (e H.DeleteMin(x); / 最小耗费边e - - ;int a = U.Find(x.u);int b = U.Find(x.v);if (a != b) / 选择边tk+ = x;U . U n i o n ( a , b ) ; D e a c t i v a t e P o s ( )
11、;H . D e a c t i v a t e ( ) ;return (k = n - 1);2. Prim 算法与 Kr u s k a l 算法类似,P r i m 算法通过每次选择多条边来创建最小生成树。选择下一条边的贪婪准则是:从剩余的边中,选择一条耗费最小的边,并且它的加入应使所有入选的边仍是一棵树。最终,在所有步骤中选择的边形成一棵树。相反,在 Kruskal 算法中所有入选的边集合最终形成一个森林。P r i m 算法从具有一个单一顶点的树 T 开始,这个顶点可以是原图中任意一个顶点。然后往 T 中加入一条代价最小的边( u , v)使 T (u , v) 仍是一棵树,这种加
12、边的步骤反复循环直到 T 中包含 n- 1 条边。注意对于边( u , v) ,u、v 中正好有一个顶点位于 T 中。P r i m 算法的伪代码如图 1 -1 4 所示。在伪代码中也包含了所输入的图不是连通图的可能,在这种情况下没有生成树。图 1 - 1 5 显示了对图 1-12a 使用 P r i m 算法的过程。把图 1 - 1 4 的伪代码细化为 C + +程序及其正确性的证明留作练习(练习 3 1) 。/ /假设网络中至少具有一个顶点设 T 为所选择的边的集合,初始化 T=设 T V 为已在树中的顶点的集合,置 T V= 1 令 E 为网络中边的集合w h i l e(E ) TV
13、且 c o st (v, n e ar (v) )的值是所有这样的 n e ar (v) 节点中最小的,则实现 P r i m 算法的时间复杂性为 O (n2 )。下一条添加到 T 中的边是这样的边:其 cost (v, near (v) 最小,且 v T V。3. Sollin 算法S o l l i n 算法每步选择若干条边。在每步开始时,所选择的边及图中的 n 个顶点形成一个生成树的森林。在每一步中为森林中的每棵树选择一条边,这条边刚好有一个顶点在树中且边的代价最小。将所选择的边加入要创建的生成树中。注意一个森林中的两棵树可选择同一条边,因此必须多次复制同一条边。当有多条边具有相同的耗费
14、时,两棵树可选择与它们相连的不同的边,在这种情况下,必须丢弃其中的一条边。开始时,所选择的边的集合为空。若某一步结束时仅剩下一棵树或没有剩余的边可供选择时算法终止。图 1 - 6 给出了初始状态为图 1-12a 时,使用 S o l l i n 算法的步骤。初始入选边数为 0 时的情形如图 13-12a 时,森林中的每棵树均是单个顶点。顶点 1,2 ,.,7 所选择的边分别是(1.6), (2,7),(3,4), (4,3), (5,4), (6,1), (7,2),其中不同的边是( 1 , 6 ),( 2 , 7 ),(3,4) 和( 5 , 4 ),将这些边加入入选边的集合后所得到的结果如
15、图 1 3 - 1 6 a 所示。下一步具有顶点集 1 , 6 的树选择边( 6 , 5 ),剩下的两棵树选择边 ( 2 , 3 ),加入这两条边后已形成一棵生成树,构建好的生成树见图 1 3 - 6 b。S o l l i n 算法的 C + +程序实现及其正确性证明留作练习(练习 3 2 )。-plot(100+t+15*cos(3.05*t),t=0200,coords=polar,axes=none,scaling=constrained); 2004-5-27 19:42:54 b 等级:职业侠客 文章:472积分:951门派:黑客帝国 注册:2003-8-28第 13 楼 练习8.
16、 针对装载问题,扩充贪婪算法,考虑有两条船的情况,算法总能产生最优解吗?9. 已知 n 个任务的执行序列。假设任务 i 需要 ti 个时间单位。若任务完成的顺序为 1,2,.,n,则任务 i 完成的时间为 ci =i j = 1tj 。任务的平均完成时间(Av e rge Completion Time, ACT)为 1nn i = 1ci 。1) 考虑有四个任务的情况,每个任务所需时间分别是( 4,2,8,1) 。若任务的顺序为 1,2,3 ,4,则 A C T 是多少?2) 若任务顺序为 2,1 ,4,3,则 A C T 是多少?3) 创建具有最小 A C T 的任务序列的贪婪算法分 n
17、步来构造该任务序列,在每一步中,从剩下的任务里选择时间最小的任务。对于 1 ),利用这种策略获得的任务顺序为 4,2,1,3,这种顺序的 A C T 是多少?4) 写一个 C + +程序实现 3) 中的贪婪策略,程序的复杂性应为 O (nl o gn),试证明之。5) 证明利用 3) 中的贪婪算法获得的任务顺序具有最小的 A C T。10. 若有两个工人执行练习 9 中的 n 个任务,需将任务分配给他们,同时他们具有自己的任务执行顺序。任务完成时间及 A C T 的定义同练习 9。使 A C T 最小化的一种可行的贪婪算法是:两个工人轮流选择任务,每次从剩余的任务中选择时间最小的任务。每个人按
18、照自己所选任务的顺序执行任务。对于练习 9 中的例子,假定工人 1 首先选择任务 4,然后工人 2 选择任务 2,工人 1 选择任务 1,最后工人 2 选择任务 3。1) 利用 C + +程序实现这种策略,其时间复杂性为多少?2) 上述的贪婪策略总能获得最小的 A C T 吗?证明结论。11. 1) 考虑有 m 个人可以执行任务,扩充练习 1 0 中的贪婪算法。2) 算法能保证获得最优解吗?证明结论。3) 用 C + +程序实现此算法,其复杂性是多少?12. 考虑例 4 - 4 的堆栈折叠问题。1) 设计一个贪婪算法,将堆栈折叠为最小数目的子堆栈,使得每个子堆栈的高度均不超过 H。2) 算法总
19、能保证得到数目最少的子堆栈吗?证明结论。3) 用 C + +代码实现 1) 的算法。4) 代码的时间复杂性是多少?13. 编写 C + +程序实现 0 / 1 背包问题,使用如下启发式方法:按价值密度非递减的顺序打包。14. 根据 k= 1 的性能受限启发式方法编写一个 C + +程序来实现 0 / 1 背包问题。15. 对于 k= 1 的情况证明用性能受限的启发式方法求解 0 / 1 背包问题会发生边界错误。16. 根据 k= 2 的性能受限启发式方法编写一个 C + +程序来实现 0 / 1 背包问题。17. 考虑 0xi 1 而不是 xi 0 , 1 的连续背包问题。一种可行的贪婪策略是
20、:按价值密度非递减的顺序检查物品,若剩余容量能容下正在考察的物品,将其装入;否则,往背包中装入此物品的一部分。1) 对于 n=3, w=100,10,10, p= 2 0 , 1 5 , 1 5 及 c= 1 0 5 ,上述装入方法获得的结果是什么?2) 证明这种贪婪算法总能获得最优解。3) 用一个 C + +程序实现此算法。18. 例 1 3 - 1 的渴婴问题是练习 1 7 中连续背包问题的一般化,将练习 1 7 的贪婪算法用于渴婴问题,算法能保证总能得到最优解吗?证明结论。19. 1) 证明当且仅当二分图没有覆盖时,图 1 3 - 7 的算法找不到覆盖。2) 给出一个具有覆盖的二分图,使
21、得图 1 3 - 7 的算法找不到最小覆盖。20. 当第一步选择了顶点 1 时,给出图 1 3 - 7 的工作过程。21. 对于二分图覆盖问题设计另外一种贪婪启发式方法,可使用如下贪婪准则:如果 B 中的某一个顶点仅被 A 中一个顶点覆盖,选择 A 中这个顶点;否则,从 A 中选择一个顶点,使得它所覆盖的未被覆盖的顶点数目最多。1) 给出这种贪婪算法的伪代码。2) 编写一个 C + +函数作为 U n d i r e c t e d 类的成员来实现上述贪婪算法。3) 函数的复杂性是多少?4) 验证代码的正确性。22. 令 G 为无向图,S 为 G 中顶点的子集,当且仅当 S 中的任意两个顶点都
22、有一条边相连时,S 为完备子图(c l i q u e) ,完备子图的大小即 S 中的顶点数目。最大完备子图( maximum clique)即具有最大项点数目的完备子图。在图中寻找最大完备子图的问题(即最大完备子图问题)是一个 N P-复杂问题。1) 给出最大完备子图问题的一种可行的贪婪算法及其伪代码。2) 给出一个能用 1) 中的启发式算法求解最大完备子图的图例,以及不能用该算法求解的一个图例。3) 将 1) 中的启发式算法实现为 Undirected:Clique(int C, int m) 共享成员,其中最大完备子图的大小返回到 m 中,最大完备子图的顶点返回到 C 中。4) 代码的复
23、杂性是多少?23. 令 G 为一无向图,S 为 G 中顶点的子集,当且仅当 S 中任意两个顶点都无边相连时, S 为无关集(independent set) 。最大无关集即是顶点数目最多的无关集。在一幅图中寻找最大无关集是一个 N P-复杂问题。按练习 2 2 的要求解决最大无关集问题。24. 对无向图 G 着色的方法是:为 G 中的顶点编号( 1 , 2 ,.) ,使得由一条边相连的两个顶点具有不同的编号。在图的着色问题中,要求利用最少的相互不同的颜色(编号)来给图 G 着色。图的着色问题也是一个 N P-复杂问题。按练习 2 2 的要求解决图着色问题。25. 证明当按路径长度的顺序产生一条
24、最短路径时,所产生的下一条最短路径总是由已产生的一条最短路径扩充一条边得到。26. 证明对于具有一条或多条具有负长度的边,图 1 3 - 11 的贪婪算法不一定能正确地计算出最短路径的长度。27. 编写一个 P a t h ( p , s , i )函数,利用函数 S h o r t e s t P a t h s 计算出的p 值,输出从顶点 s 到顶点 i 的一条最短路径。函数的复杂性是多少?28. 若把有向图作为 L i n k e d w D i g r a p h 类的一个成员,重写程序 1 3 - 5,函数应作为该类的一个成员。函数的复杂性是多少?29. 若把有向图作为 L i n
25、k e d W D i g r a p h 类的一个成员且仅有 O (n)条边,重写程序 1 3 - 5,L 用最小堆来实现。函数的复杂性是多少?30. 从 N e t w o r k 类(见程序 1 2 - 1 5)派生出一个新的模板类 D N e t w o r k(有向网络) ,这个类仅包含应用于有向网络的所有函数。为该类定义一个 S h o r t e s t P a t h s 函数,使得它与有向网络的描述形式无关,尤其适用于耗费邻接矩阵及邻接链表描述方法。在函数的实现过程中可利用原来的遍历函数,也可根据需要定义新的遍历函数。函数的复杂性应为 O (n2 ),其中 n 是顶点的数目,
26、试证明之。*31. 1) 给出 P r i m 算法(如图 1 3 - 1 4 所示)的一种正确性证明。2) 将图 1 3 - 1 4 细化为一个 C + +程序 U N e t w o r k : : P r i m,其复杂性应为 O (n2 )。3) 证明程序的复杂性确实是 O(n2 )。*32. 1) 证明对于任意连通无向图,S o l l i n 算法总能找到一个最小耗费生成树。2) 在 S o l l i n 算法中,最大的步骤数是多少?试用图中顶点数 n 来表示。3) 编写一个 C + +程序 U N e t w o r k : : S o l l i n,使用 S o l l i
27、 n 算法找到一棵最小生成树。4) 程序的复杂性是多少?*33. 令 T 为一棵每条边均带有长度的树(不一定是二叉树) 。令 S 为 T 中顶点的子集,并令 T / S 为从 T 中删除 S 中的顶点所得到的森林。我们希望能找到具有最小走势的子集 S,使得 T / S 中没有从根到叶的距离大于 d 的森林。1) 给出一种寻找最小走势子集 S 的贪婪算法(提示:从叶节点开始向根移动) 。2) 证明算法的正确性。3) 算法的复杂性是多少?如果它不是 T 中顶点数的线性函数,则重新设计算法,使其复杂性是线性的。*34. 令 T / S 表示将 S 中的每个顶点复制两份而获得的森林,其中父节点的指针指
28、向一个复本,而另一复本的指针指向其儿子。针对这种情况再做练习 3 3。( 说明:本资料是根据数据结构、算法与应用 (美,Sartaj Sahni 著)一书第 13-17 章编辑、改写的。考虑到因特网传输速度等因素,大部分插图和公式不得不被删除。对于内容不连贯之处,请网友或读者参阅该书,敬请原谅。 )-plot(100+t+15*cos(3.05*t),t=0200,coords=polar,axes=none,scaling=constrained); 2004-5-27 19:43:19 lantian3 等级:新手上路 文章:2积分:52门派:nudter 注册:2004-6-5第 14
29、楼 怎么没人顶?我先来2004-6-5 10:49:29 jolin 等级:新手上路 文章:22积分:101注册:2004-3-26第 15 楼 虽然我还没看,但是还是要顶一顶2004-6-5 18:12:35 afliboy 等级:新手上路 文章:1积分:51门派:nudter 注册:2004-6-5第 16 楼 狂顶2004-6-5 23:41:53 king8egg 等级:新手上路 文章:2积分:53门派:nudter 注册:2004-6-3第 17 楼 不错不错,看你发了这么多, 不顶不好意思啊,以后有时间再慢慢看吧2004-6-6 21:49:31 寒枫 等级:新手上路 文章:8积分
30、:64门派:nudter 注册:2004-6-8第 18 楼 练习做完了是不是直接跟帖就行?2004-6-8 21:15:26 本主题贴数 18 分页:9 1 2 : 跳转论坛至.数学建模 数模竞赛 新手入门 数学工具 资源与检索学术区 数学思想 编程交流 学术杂谈 English Fans休闲专区 灌水搞笑专区 神秘园本站站务 站务讨论 数模管理区*快速回复:贪婪算法发贴表情段落格式 普通格式标题 1 标题 2 标题 3 标题 4 标题 5 标题 6 标题 7已编排格式地址 字体宋体黑体楷体仿宋隶书幼圆新宋体细明体 ArialArial BlackCourierVerdanaWide Lat
31、inWingdings 字号 1234567 第 1 页,共 7 页, 49 个显示签名 内容限制:字节. 管理选项: 专题管理 | 修复 | 锁定 | 解锁 | 提升 | 跟贴管理 | 删除 | 移动 | 设置固顶 | 奖励 | 惩罚 | 发布公告 Copyright 2002 - 2004 Shumo.Com执行时间:156.25000 毫秒。查询数据库 9 次。当前模板样式:默认模板 中国数学建模-编程交流- 贪婪算法wh-ee 重登录 隐身 用户控制面板 搜索 风格 论坛状态 论坛展区 社区服务 社区休闲 网站首页 退出 VC+,C,Perl,Asp.编程学习, 算法介绍. 我的收件箱
32、 (0) 中国数学建模 学术区 编程交流 贪婪算法 您是本帖的第 889 个阅读者 * 贴子主题:贪婪算法 b 等级:职业侠客 文章:470积分:956门派:黑客帝国 注册:2003-8-28鲜花(0) 鸡蛋(0) 楼主 贪婪算法第 1 章 贪婪算法虽然设计一个好的求解算法更像是一门艺术,而不像是技术,但仍然存在一些行之有效的能够用于解决许多问题的算法设计方法,你可以使用这些方法来设计算法,并观察这些算法是如何工作的。一般情况下,为了获得较好的性能,必须对算法进行细致的调整。但是在某些情况下,算法经过调整之后性能仍无法达到要求,这时就必须寻求另外的方法来求解该问题。本章首先引入最优化的概念,然
33、后介绍一种直观的问题求解方法:贪婪算法。最后,应用该算法给出货箱装船问题、背包问题、拓扑排序问题、二分覆盖问题、最短路径问题、最小代价生成树等问题的求解方案。1.1 最优化问题本章及后续章节中的许多例子都是最优化问题( optimization problem) ,每个最优化问题都包含一组限制条件( c o n s t r a i n t)和一个优化函数( optimization function) ,符合限制条件的问题求解方案称为可行解( feasible solution) ,使优化函数取得最佳值的可行解称为最优解(optimal solution) 。例 1-1 渴婴问题 有一个非常渴
34、的、聪明的小婴儿,她可能得到的东西包括一杯水、一桶牛奶、多罐不同种类的果汁、许多不同的装在瓶子或罐子中的苏打水,即婴儿可得到 n 种不同的饮料。根据以前关于这 n 种饮料的不同体验,此婴儿知道这其中某些饮料更合自己的胃口,因此,婴儿采取如下方法为每一种饮料赋予一个满意度值:饮用 1 盎司第 i 种饮料,对它作出相对评价,将一个数值 si 作为满意度赋予第 i 种饮料。通常,这个婴儿都会尽量饮用具有最大满意度值的饮料来最大限度地满足她解渴的需要,但是不幸的是:具有最大满意度值的饮料有时并没有足够的量来满足此婴儿解渴的需要。设 ai 是第 i 种饮料的总量(以盎司为单位) ,而此婴儿需要 t 盎司
35、的饮料来解渴,那么,需要饮用 n 种不同的饮料各多少量才能满足婴儿解渴的需求呢?设各种饮料的满意度已知。令 xi 为婴儿将要饮用的第 i 种饮料的量,则需要解决的问题是:找到一组实数 xi(1i n) ,使 n i = 1si xi 最大,并满足:n i=1xi =t 及 0xiai 。需要指出的是:如果 n i = 1ai k。寻找 1 ,n范围内最小的整数 j,使得 xjyj 。若没有这样的 j 存在,则 n i= 1xi =n i = 1yi 。如果有这样的 j 存在,则 jk ,否则 y 就不是一个可行解,因为 xjyj ,xj = 1 且 yj = 0。令 yj = 1,若结果得到的
36、 y 不是可行解,则在 j+ 1 ,n范围内必有一个 l 使得 yl = 1。令 yl = 0,由于wjwl ,则得到的 y 是可行的。而且,得到的新 y 至少与原来的 y 具有相同数目的 1。经过数次这种转化,可将 y 转化为 x。由于每次转化产生的新 y 至少与前一个 y 具有相同数目的 1,因此 x 至少与初始的 y 具有相同的数目 1。货箱装载算法的 C + +代码实现见程序 1 3 - 1。由于贪婪算法按货箱重量递增的顺序装载,程序 1 3 - 1 首先利用间接寻址排序函数 I n d i r e c t S o r t 对货箱重量进行排序(见 3 . 5 节间接寻址的定义) ,随后
37、货箱便可按重量递增的顺序装载。由于间接寻址排序所需的时间为 O (nl o gn)(也可利用 9 . 5 . 1 节的堆排序及第 2 章的归并排序) ,算法其余部分所需时间为 O (n),因此程序 1 3 - 1 的总的复杂性为 O (nl o gn)。程序 13-1 货箱装船templatevoid ContainerLoading(int x, T w, T c, int n)/ 货箱装船问题的贪婪算法/ xi = 1 当且仅当货箱 i 被装载, 10 时总的时间开销为 O (nk+1 )。实际观察到的性能要好得多。-plot(100+t+15*cos(3.05*t),t=0200,coo
38、rds=polar,axes=none,scaling=constrained); 2004-5-27 19:39:53 b 等级:职业侠客 文章:470积分:956门派:黑客帝国 注册:2003-8-28第 6 楼 1.3.3 拓扑排序一个复杂的工程通常可以分解成一组小任务的集合,完成这些小任务意味着整个工程的完成。例如,汽车装配工程可分解为以下任务:将底盘放上装配线,装轴,将座位装在底盘上,上漆,装刹车,装门等等。任务之间具有先后关系,例如在装轴之前必须先将底板放上装配线。任务的先后顺序可用有向图表示称为顶点活动( Activity On Vertex, AOV)网络。有向图的顶点代表任务
39、,有向边(i, j) 表示先后关系:任务 j 开始前任务 i 必须完成。图 1 - 4 显示了六个任务的工程,边( 1 , 4)表示任务 1 在任务 4 开始前完成,同样边( 4 , 6)表示任务 4 在任务 6 开始前完成,边(1 , 4)与(4 , 6)合起来可知任务 1 在任务 6 开始前完成,即前后关系是传递的。由此可知,边(1 , 4)是多余的,因为边(1 , 3)和(3 , 4)已暗示了这种关系。在很多条件下,任务的执行是连续进行的,例如汽车装配问题或平时购买的标有“需要装配”的消费品(自行车、小孩的秋千装置,割草机等等) 。我们可根据所建议的顺序来装配。在由任务建立的有向图中,边
40、( i, j)表示在装配序列中任务 i 在任务 j 的前面,具有这种性质的序列称为拓扑序列(topological orders 或 topological sequences)。根据任务的有向图建立拓扑序列的过程称为拓扑排序(topological sorting) 。图 1 - 4 的任务有向图有多种拓扑序列,其中的三种为 1 2 3 4 5 6, 1 3 2 4 5 6 和 2 1 5 3 4 6,序列 1 4 2 3 5 6 就不是拓扑序列,因为在这个序列中任务 4 在 3 的前面,而任务有向图中的边为( 3 , 4) ,这种序列与边( 3 , 4)及其他边所指示的序列相矛盾。可用贪婪
41、算法来建立拓扑序列。算法按从左到右的步骤构造拓扑序列,每一步在排好的序列中加入一个顶点。利用如下贪婪准则来选择顶点:从剩下的顶点中,选择顶点 w,使得 w 不存在这样的入边( v,w) ,其中顶点 v 不在已排好的序列结构中出现。注意到如果加入的顶点 w 违背了这个准则(即有向图中存在边( v,w)且 v 不在已构造的序列中) ,则无法完成拓扑排序,因为顶点 v 必须跟随在顶点 w 之后。贪婪算法的伪代码如图 1 3 - 5 所示。while 循环的每次迭代代表贪婪算法的一个步骤。现在用贪婪算法来求解图 1 - 4 的有向图。首先从一个空序列 V 开始,第一步选择 V 的第一个顶点。此时,在有
42、向图中有两个候选顶点 1 和 2,若选择顶点 2,则序列 V = 2,第一步完成。第二步选择 V 的第二个顶点,根据贪婪准则可知候选顶点为 1 和 5,若选择 5,则 V = 2 5。下一步,顶点 1 是唯一的候选,因此 V = 2 5 1。第四步,顶点 3 是唯一的候选,因此把顶点 3 加入 V得到 V = 2 5 1 3。在最后两步分别加入顶点 4 和 6 ,得 V = 2 5 1 3 4 6。1. 贪婪算法的正确性为保证贪婪算法算的正确性,需要证明: 1) 当算法失败时,有向图没有拓扑序列; 2) 若算法没有失败,V 即是拓扑序列。2) 即是用贪婪准则来选取下一个顶点的直接结果, 1)
43、的证明见定理 1 3 - 2,它证明了若算法失败,则有向图中有环路。若有向图中包含环 qj qj + 1.qk qj , 则它没有拓扑序列,因为该序列暗示了 qj 一定要在 qj 开始前完成。定理 1-2 如果图 1 3 - 5 算法失败,则有向图含有环路。证明注意到当失败时| V | S;for (i = 1; i = n; i+)if (!InDegreei) S.Add(i);/ 产生拓扑次序i = 0; / 数组 v 的游标while (!S.IsEmpty() / 从堆栈中选择int w; / 下一个顶点S . D e l e t e ( w ) ;vi+ = w;int u = B
44、egin(w);while (u) / 修改入度I n D e g r e e u - - ;if (!InDegreeu) S.Add(u);u = NextVe r t e x ( w ) ; D e a c t i v a t e P o s ( ) ;delete InDegree;return (i = n);-plot(100+t+15*cos(3.05*t),t=0200,coords=polar,axes=none,scaling=constrained); 2004-5-27 19:40:10 b 等级:职业侠客 文章:470积分:956门派:黑客帝国 注册:2003-8-2
45、8第 7 楼 1.3.4 二分覆盖二分图是一个无向图,它的 n 个顶点可二分为集合 A 和集合 B,且同一集合中的任意两个顶点在图中无边相连(即任何一条边都是一个顶点在集合 A 中,另一个在集合 B 中) 。当且仅当 B 中的每个顶点至少与 A 中一个顶点相连时,A 的一个子集 A 覆盖集合 B(或简单地说,A 是一个覆盖) 。覆盖 A 的大小即为 A 中的顶点数目。当且仅当 A 是覆盖 B 的子集中最小的时,A 为最小覆盖。例 1-10 考察如图 1 - 6 所示的具有 1 7 个顶点的二分图, A=1, 2, 3, 16, 17和 B=4, 5, 6, 7, 8, 9,10, 11, 12
46、, 13, 14, 15,子集 A = 1 , 1 6 , 1 7 是 B 的最小覆盖。在二分图中寻找最小覆盖的问题为二分覆盖( b i p a r t i t e - c o v e r)问题。在例 1 2 - 3 中说明了最小覆盖是很有用的,因为它能解决“在会议中使用最少的翻译人员进行翻译”这一类的问题。二分覆盖问题类似于集合覆盖( s e t - c o v e r)问题。在集合覆盖问题中给出了 k 个集合 S= S1 , S2 ,., Sk ,每个集合 Si 中的元素均是全集 U 中的成员。当且仅当i SSi =U 时,S 的子集 S 覆盖 U,S 中的集合数目即为覆盖的大小。当且仅当
47、没有能覆盖 U 的更小的集合时,称 S 为最小覆盖。可以将集合覆盖问题转化为二分覆盖问题(反之亦然) ,即用A 的顶点来表示 S1 , ., Sk ,B 中的顶点代表 U 中的元素。当且仅当 S 的相应集合中包含 U 中的对应元素时,在 A 与 B 的顶点之间存在一条边。例 1 - 11 令 S= S1,. . .,S5 , U= 4,5 ,. . .,15, S1 = 4,6,7,8 ,9,1 3 ,S2 = 4,5 ,6,8 ,S3 = 8,1 0, 1 2,1 4,1 5 ,S4 = 5,6,8 ,1 2, 1 4,1 5 ,S5 = 4,9,1 0,11 。S = S1,S4,S5 是
48、一个大小为 3 的覆盖,没有更小的覆盖, S 即为最小覆盖。这个集合覆盖问题可映射为图 1-6 的二分图,即用顶点1, 2,3,1 6 和 1 7 分别表示集合 S1,S2 ,S3,S4 和 S5,顶点 j 表示集合中的元素 j,4j1 5。集合覆盖问题为 N P-复杂问题。由于集合覆盖与二分覆盖是同一类问题,二分覆盖问题也是 N P-复杂问题。因此可能无法找到一个快速的算法来解决它,但是可以利用贪婪算法寻找一种快速启发式方法。一种可能是分步建立覆盖 A ,每一步选择 A 中的一个顶点加入覆盖。顶点的选择利用贪婪准则:从 A中选取能覆盖 B 中还未被覆盖的元素数目最多的顶点。例 1-12 考察图 1 - 6 所示的二分图,初始化 A = 且 B 中没有顶点被覆盖,顶点 1 和 1 6 均能覆盖 B 中的六个顶点,顶点 3 覆盖五个,顶点 2 和 1 7 分别覆盖四个。因此,在第一步往 A 中加入顶点 1 或 1 6,若加入顶点 1 6,则它覆盖的顶点为 5 , 6 , 8 , 1 2 , 1 4 ,