acm图论一.ppt
《acm图论一.ppt》由会员分享,可在线阅读,更多相关《acm图论一.ppt(88页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、图论基础图论基础 并查集及其应用并查集及其应用 最小生成树最小生成树: Kruskal Kruskal算法算法,PrimPrim算法算法 最短路径:最短路径:DijkstraDijkstra算法,算法,FloyedFloyed算法,算法,Bellman-FordBellman-Ford算法算法 EulerEuler回路,回路,HamiltonHamilton回路回路 二分图的最大匹配二分图的最大匹配学习图论的误区学习图论的误区1 1 模板模板只知道每种算法的模板,不深究其中的方法。只知道每种算法的模板,不深究其中的方法。 这样平时做题时可这样平时做题时可以做的省事,但是在比赛时就会觉得这个题是
2、用这个模板但发现不了陷以做的省事,但是在比赛时就会觉得这个题是用这个模板但发现不了陷阱一直阱一直WAWA。例如最大匹配。例如最大匹配- -最小覆盖原则,如果只知道最大匹配算法模最小覆盖原则,如果只知道最大匹配算法模板,遇到一个最小覆盖的问题就不会做了,实际上这两个问题是等价的。板,遇到一个最小覆盖的问题就不会做了,实际上这两个问题是等价的。如果连定义都说不明白的话,会显得你很如果连定义都说不明白的话,会显得你很lowlow。2 2 递归与非递归递归与非递归图论有很多算法可以用递归实现。例如匈牙利算法,递归与非递归图论有很多算法可以用递归实现。例如匈牙利算法,递归与非递归实现都能解决最大匹配问题
3、,而且递归可读性与条例性更强。但在顶点实现都能解决最大匹配问题,而且递归可读性与条例性更强。但在顶点数量很大时,非递归算法就能体现出他的优势:无须担心栈溢出。所以数量很大时,非递归算法就能体现出他的优势:无须担心栈溢出。所以希望,学有余力的同学能够了解一下你所掌握的算法的非递归实现。希望,学有余力的同学能够了解一下你所掌握的算法的非递归实现。3 3 彻底明白算法后,每次也只是直接复制代码彻底明白算法后,每次也只是直接复制代码 这样的结果就是:忘的非常快。非常快。快。这样的结果就是:忘的非常快。非常快。快。每次敲代码都是对算法的重新复习,甚至可以重新审视自己的算法实现,每次敲代码都是对算法的重新
4、复习,甚至可以重新审视自己的算法实现,从而优化自己的代码。记得耀哥就敲过这些算法做消遣,而不是玩扫雷。从而优化自己的代码。记得耀哥就敲过这些算法做消遣,而不是玩扫雷。并查集并查集引例:亲戚(relatives)问题或许你并不知道,你的某个朋友是你的亲戚。他可能是你的或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子!曾祖父的外公的女婿的外甥女的表姐的孙子!如果能得到完整的家谱,如果能得到完整的家谱,判断两个人是否亲戚判断两个人是否亲戚应该是可行的应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家,但如果两个人的最近公共祖先与他们相隔好几代,
5、使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。况下,最好的帮手就是计算机。并查集为了将问题简化,你将得到一些亲戚关系的信息,如为了将问题简化,你将得到一些亲戚关系的信息,如MarryMarry和和TomTom是亲戚,是亲戚,TomTom和和BenBen是亲戚,等等。从这些信息中,你是亲戚,等等。从这些信息中,你可以推出可以推出MarryMarry和和BenBen是亲戚。是亲戚。请写一个程序,对于有关亲戚关系的提问,以最快的速度给请写一个程序,对于有关亲戚关系的提问,以最快的速度给出答案。出答案。 并查集
6、Input:Input:n 第一部分以N N,M M开始。N N为问题涉及的人的个数(1 (1 N N 20000) 20000)。这些人的编号为1,2,3, N1,2,3, N。下面有M M行(1 (1 M M 1 000 000) 1 000 000),每行有两个数a ai i, b, bi i,表示已知a ai i和b bi i是亲戚。n 第二部分以Q Q开始。以下Q Q行有Q Q个询问(1 (1 Q Q 1 000 000) 1 000 000),每行为c ci i, d, di i,表示询问c ci i和d di i是否为亲戚。n输出:n 对于每个询问c ci i, d, di i,
7、输出一行:若c ci i和d di i为亲戚,则输出“Yes”Yes”,否则输出“No”No”。并查集n输入样例:n10 710 7n2 42 4n5 75 7n1 31 3n8 98 9n1 21 2n5 65 6n2 32 3n3 3n3 43 4n7 107 10n8 98 9输出样例:输出样例:YesYesNoNoYesYes 问题分析 我们可以将每个人抽象成为一个点,数据给出M个边的关系,两个人是亲戚的时候两点间有一条边。很自然地就得到了一个有N个顶点M条边的图论模型,注意到传递关系,在此无向图中的一个连通分支中的任意点之间都是亲戚。 对于最后的Q个提问,即判断所提问的两个顶点是否在
8、同一个连通分支中。问题分析 这样的解题思路很清楚,对于输入的N个点M条边,建立一个无向图(保存边),找出所有的连通块(遍历算法),然后根据提问逐个进行判断。 但是本题的数据范围很大,1 N 20000 , 1 M 1 000 000,不管是从空间上看还是从时间上看,都很难接受。问题分析 再进一步考虑,如果把题目的要求改一改,对于边和提问相间输入,即把题目改成:第一行是N,M。N为问题涉及的人的个数,下面有M行,每行有三个数ki,ai,b,其含义如下:ai和bi表示两个元素,ki为0或1ki为1时,表示这是一条边的信息,即表示ai和bi是亲戚关系ki为0时,表示这是一个提问,要你根据此行以前所得
9、到的信息,判断ai和bi是否是亲戚,对于每条提问回答Yes或No。 问题分析 这个问题比原问题更复杂些(复杂在何处?),需要在任何时候回答提问的两个人的关系,是一种动态判断。并且对于信息提示还要能立即合并两个连通分支。采用连通图思想在实现上就有点困难,因为要实时地表示人与人之间的关系。问题分析 可以用集合的思路来考虑这个问题(从逻辑上来看,这的确是个集合问题): 对每个人都建立一个集合; 开始的时候,集合元素是这个人本身,表示开始时不知道任何人是他的亲戚; 以后每次给出一个亲戚关系时,就将两个集合合并。这样实时地得到了在当前状态下的集合关系。如果有提问,即在当前得到的结果中,看两个元素是否属于
10、同一集合。输入关系输入关系 分离集合分离集合初始状态初始状态 1234567891012345678910(2,4) 12,435678910(2,4) 12,435678910(5,7) 12,435,768910(5,7) 12,435,768910(1,3)(1,3) 1,32,45,768910 1,32,45,768910(8,9) 1,32,45,768,910(8,9) 1,32,45,768,910(1,2) 1,2,3,45,768,910(1,2) 1,2,3,45,768,910(5,6) 1,2,3,45,6,78,910(5,6) 1,2,3,45,6,78,910(
11、2,3) 1,2,3,45,6,78,910(2,3) 1,2,3,45,6,78,910 由上图可以看出,由上图可以看出,操作是在集合操作是在集合的基础上进行的的基础上进行的,在操作过程中,我,在操作过程中,我们没有保存任何一条边,而且每一步们没有保存任何一条边,而且每一步得到的划分方式是动态的。得到的划分方式是动态的。( (为什么可为什么可以采用集合?用集合还有什么问题?以采用集合?用集合还有什么问题?) ) 如何来实现以上的算法思想呢?这就如何来实现以上的算法思想呢?这就用到了用到了并查集并查集这种数据结构。这种数据结构。并查集的基本思想并查集的基本思想 什么叫并查集? 并查集(unio
12、n-find set)是一种用于分离集合(disjoint-set)的操作算法。学习一种新算法需要了解哪些方面?(特性/本质、存储表示/实现、操作、应用)。它所处理的是“集合”之间的关系,即动态地维护和处理集合元素之间复杂的关系。并查集的基本思想 当给出两个元素的一个无序对(a, b)时,需要快速“合并”a和b分别所在的集合,这其间需要反复“查找”某元素(如a和b)所在的集合,然后才能合并。 “并”、“查”和“集”三字由此而来。并查集的基本思想 在这种数据类型中,n个不同的元素被分为若干组。每组是一个集合,这种集合叫做分离集合(disjoint set)。 并查集支持查找一个元素所属的集合,以
13、及将两个元素各自所属的集合进行合并等操作。并查集支持的操作 动态集合中的每一元素是由一个对象来表示的,设x表示一个对象,并查集的实现一般需要支持如下操作: MAKE-SET(x)建立一个新的集合 UNION(x, y)将包含x和y的动态集合(例如Sx和Sy)合并为一个新的集合,假定在此操作前这两个集合是分离的。 FIND-SET(x) 返回一个包含x的集合的代表。并查集的实现及优化并查集的数据结构实现方法很多,一般用的比较多的是数组实现、链表实现和树实现。 并查集的数组实现 用数组si记录元素 i 所属集合的编号。 各操作的实现如下: MAKE-SET(x):初始化只需做 si := i; F
14、IND-SET(x):查找元素所属的集合时,只需读出si,时间复杂度为O(1)。 UNION(x, y):合并两元素各自所属的集合时,需要将数组中属于其中一个集合的元素所对应的数组元素值全部改为另一个集合的 编号值,时间复杂度为O(n)。并查集的数组实现 实现简单,实际使用较多,但合并的代价太大。(diaosi用)。 在最坏情况下,所有集合合并成一个集合的总代价会达到O(n2)(Why?)。 并查集的数组实现 输入样例:输入样例:10 710 72 42 45 75 71 31 38 98 91 21 25 65 62 32 33 33 43 47 107 108 98 9UNIONUNION
15、(x,yx,y)过程演示)过程演示:S S1 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10S S1 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10并查集的链表实现 每个集合对应一个链表,它有一个表头,每一个元素有一个指针指向表头,表明了它所属的类,另有一个指针指向它的下一个元素。 同时,为了方便实现,再设一个指针last表示链表中的最后一个元素(表尾)。并查集的链表实现 此时,各并查集操作的实现如下: MAKE-SET(x): Sx.head := x; Sx.next := 0; FIND-SET(x): return Sx.he
16、ad 两个过程的时间复杂度都为O(1)。并查集的链表实现 UNION(x, y): 假设UNION(x, y)的参数是有序的,即把y属于的集合合并到x的集合上去,有两种实现方法:(1)简单实现(2)快速实现 UNION(x,y)操作的简单实现 不考虑任何因素,出现FIND-SET(x) FIND-SET(y)时,直接将y的表头接到x的表尾,同时将y所在集合中所有元素的head值设为FIND-SET(x)。同时x的表尾也应该设为原y表的表尾。 注意:last指针其实只要在表头结点中记录即可,因为每一次查到FIND-SET(x)都可以得到表头元素。而链表中其他元素重新记录last是无意义的。UNI
17、ON(x,y)操作的简单实现 我们总是把y接到x里去,那么如果y所在的集合非常大,每次赋值的代价就会非常高,考虑输入数据的特殊性,比如出现输入为: (2,1),(3,1),(4,1), (5,1),(6,1) , ,(n,1) 最坏情况下时间复杂度为O(n2)。 UNION(x,y)操作的快速实现 上述简单实现非常不理想,针对y可能比较大的这个问题,可以很快产生一个聪明的想法:不妨比较x和y所在集合的大小,从而作出选择,把较短的链表接在较长的尾部,这样效果是一样的,但代价肯定不比原来差。这就是快速实现的思路。UNION(x,y)操作的快速实现 可以在node里多设一个域number,用来记录此
18、条链表中成员的个数。显然number记录在表头元素中即可,将两表合并的时候,只要将表的number相加,因此维护起来是非常方便的。 这种快速实现的方法可以称为加权启发式合并,这里的权就是指所记录的number。 UNION(x,y)操作的快速实现 当有n个元素时,可以证明这种方法的效率在UNION上的花费(即重新赋值的次数)的上界是O(nlog2n): 考虑一个固定的对象x,当x的代表指针(head)被更新时,x必是属于一个较小的集合,因此,x的代表指针第一次更新后,结果集合必然至少有2个元素。UNION(x,y)操作的快速实现u类似地,下一次更新后,x所在的集合至少有4个元素。u继续下去,可
19、以发现,x的代表指针最多被更新log2n次,因为当x所在集合元素已经等于n以后,不可能再发生UNION操作。u所以,总共有n个元素时,操作的总次数不超过nlog2n次。这就保证了整个算法的复杂度是理想的。 并查集的链表实现是一种非常容易接受的算法,并且它并查集的链表实现是一种非常容易接受的算法,并且它的效率也是令人满意的。其实,它的思路和数组完全一样,的效率也是令人满意的。其实,它的思路和数组完全一样,所以实际使用较少。所以实际使用较少。 合并两个集合时的实现过程如下:合并两个集合时的实现过程如下:UNION(x,y) UNION(x,y) x = FIND-SET(x); x = FIND-
20、SET(x); y = FIND-SET(y); y = FIND-SET(y); if x.number y.number then UNION(x,y) if x.number y.number then UNION(x,y) else UNION(y,x); else UNION(y,x); 并查集的树实现 (重要) 我们用有根树来表示集合,树中的每个结点包含集合的一个成员,每棵树表示一个集合。 多个集合形成森林,以每棵树的树根作为集合的代表,并且根结点的父结点指向其自身,树上的其他结点的父结点指向根结点。 下图表示了这种关系,它包含两个集合,即下图表示了这种关系,它包含两个集合,即b,
21、c,e,hb,c,e,h和和d,f,gd,f,g,它们分别以,它们分别以c c和和f f作为代表:作为代表: c h e b d f g 并查集的树实现 这种树结构也可以简单地用静态数组实现,设px表示元素 x 所指向的父亲。MAKE-SET(x): px=x;FIND-SET(x): 要从x开始,不断向上寻找它的父亲,直到找到根为止。UNION(x, y):只要使一棵树的根指向另一棵树的根即可。 并查集的树实现 下下图描述了查找一个结点的过程(黑色结点为当前查找结点)。图描述了查找一个结点的过程(黑色结点为当前查找结点)。 查找算法的复杂度取决于查找结点的深度,假设查找结点的深度为查找算法的
22、复杂度取决于查找结点的深度,假设查找结点的深度为h h,则算法的时间复杂度为则算法的时间复杂度为O(h)O(h)。 并查集的并查集的树树实现实现 要完成上述合并,首先要找到两棵分离集合树的根结点,这可以通过调用要完成上述合并,首先要找到两棵分离集合树的根结点,这可以通过调用两次查找算法得到。得到根结点后,只需改变其中一个根结点,令它的父结点两次查找算法得到。得到根结点后,只需改变其中一个根结点,令它的父结点为另一个根结点即可,代价为为另一个根结点即可,代价为O(1)O(1)。因此,整个算法的复杂度主要在查找根结。因此,整个算法的复杂度主要在查找根结点部分,为点部分,为O(h)O(h)。 优化查
23、找与合并算法优化查找与合并算法 前面提到,分离集合森林的查找与合并的时间前面提到,分离集合森林的查找与合并的时间复杂度都是复杂度都是O(h)O(h)。也就是说,查找与合并的时间复。也就是说,查找与合并的时间复杂度主要取决于树的深度。就平均情况而言,树的杂度主要取决于树的深度。就平均情况而言,树的深度应该在深度应该在loglog2 2n n的数量级,的数量级,n n为结点个数,所以分为结点个数,所以分离集合森林查找与合并的平均时间复杂度为离集合森林查找与合并的平均时间复杂度为O(logO(log2 2n)n)。但是,在最坏情况下,一棵树的深度可。但是,在最坏情况下,一棵树的深度可能达到能达到n
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- acm 图论一
限制150内