当前位置:首页 » 操作系统 » 最大匹配图算法

最大匹配图算法

发布时间: 2022-10-18 18:13:44

A. 图像匹配的算法

迄今为止,人们已经提出了各种各样的图像匹配算法,但从总体上讲,这些匹配算法可以分成关系结构匹配方法、结合特定理论工具的匹配方法、基于灰度信息的匹配方法、基于亚像元匹配方法、基于内容特征的匹配方法五大类型 基于内容特征的匹配首先提取反映图像重要信息的特征,而后以这些特征为模型进行匹配。局部特征有点、边缘、线条和小的区域,全局特征包括多边形和称为结构的复杂的图像内容描述。特征提取的结果是一个含有特征的表和对图像的描述,每一个特征由一组属性表示,对属性的进一步描述包括边缘的定向和弧度,边与线的长度和曲率,区域的大小等。除了局部特征的属性外,还用这些局部特征之间的关系描述全局特征,这些关系可以是几何关系,例如两个相邻的三角形之间的边,或两个边之间的距离可以是辐射度量关系,例如灰度值差别,或两个相邻区域之间的灰度值方差或拓扑关系,例如一个特征受限于另一个特征。人们一般提到的基于特征的匹配绝大多数都是指基于点、线和边缘的局部特征匹配,而具有全局特征的匹配实质上是我们上面提到的关系结构匹配方法。特征是图像内容最抽象的描述,与基于灰度的匹配方法比,特相对于几何图像和辐射影响来说更不易变化,但特征提取方法的计算代价通常较,并且需要一些自由参数和事先按照经验选取的闭值,因而不便于实时应用同时,在纹理较少的图像区域提取的特征的密度通常比较稀少,使局部特征的提 取比较困难。另外,基于特征的匹配方法的相似性度量也比较复杂,往往要以特征属性、启发式方法及闭方法的结合来确定度量方法。基于图像特征的匹配方法可以克服利用图像灰度信息进行匹配的缺点,由于图像的特征点比象素点要少很多,因而可以大大减少匹配过程的计算量同时,特征点的匹配度量值对位置的变化比较敏感,可以大大提高匹配的精确程度而且,特征点的提取过程可以减少噪声的影响,对灰度变化,图像形变以及遮挡等都有较好的适应能力。所以基于图像特征的匹配在实际中的应用越来越广-泛。所使用的特征基元有点特征明显点、角点、边缘点等、边缘线段等。

B. 求一般图的最大权匹配的算法(最好详细一些,注意数最大权匹配),高分求助!

Algorithmus 3.3 Kruskal's Algorithm 时间复杂度 O(mlog n)
m边数 n点数
输入: 图 G = (V;E) 和 权c : E -》R.
输出: 一棵最优树 T.
begin
把所有边以权的大小按从小到大排序
T := (VG,ET ) := (VG, 空);
for i = 1 to m do
if T + ei 没有圈 then
T := (VG;ET 并上 {ei});
if end
for end
end

或者 Algorithmus 3.5 Prim's Algorithm 时间复杂度O(m+n log n)

C. 衡量算法效率的方法与准则

算法效率与分析
数据结构作为程序设计的基础,其对算法效率的影响必然是不可忽视的。本文就如何合理选择数据结构来优化算法这一问题,对选择数据结构的原则和方法进行了一些探讨。首先对数据逻辑结构的重要性进行了分析,提出了选择逻辑结构的两个基本原则;接着又比较了顺序和链式两种存储结构的优点和缺点,并讨论了选择数据存储结构的方法;最后本文从选择数据结构的的另一角度出发,进一步探讨了如何将多种数据结构进行结合的方法。在讨论方法的同时,本文还结合实际,选用了一些较具有代表性的信息学竞赛试题举例进行了分析
【正文】一、引论
“数据结构+算法=程序”,这就说明程序设计的实质就是对确定的问题选择一种合适的数据结构,加上设计一种好的算法。由此可见,数据结构在程序设计中有着十分重要的地位。
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。因为这其中的“关系”,指的是数据元素之间的逻辑关系,因此数据结构又称为数据的逻辑结构。而相对于逻辑结构这个比较抽象的概念,我们将数据结构在计算机中的表示又称为数据的存储结构。
建立问题的数学模型,进而设计问题的算法,直至编出程序并进行调试通过,这就是我们解决信息学问题的一般步骤。我们要建立问题的数学模型,必须首先找出问题中各对象之间的关系,也就是确定所使用的逻辑结构;同时,设计算法和程序实现的过程,必须确定如何实现对各个对象的操作,而操作的方法是决定于数据所采用的存储结构的。因此,数据逻辑结构和存储结构的好坏,将直接影响到程序的效率。

二、选择合理的逻辑结构

在程序设计中,逻辑结构的选用就是要分析题目中的数据元素之间的关系,并根据这些特定关系来选用合适的逻辑结构以实现对问题的数学描述,进一步解决问题。逻辑结构实际上是用数学的方法来描述问题中所涉及的操作对象及对象之间的关系,将操作对象抽象为数学元素,将对象之间的复杂关系用数学语言描述出来。
根据数据元素之间关系的不同特性,通常有以下四种基本逻辑结构:集合、线性结构、树形结构、图状(网状)结构。这四种结构中,除了集合中的数据元素之间只有“同属于一个集合”的关系外,其它三种结构数据元素之间分别为“一对一”、“一对多”、“多对多”的关系。
因此,在选择逻辑结构之前,我们应首先把题目中的操作对象和对象之间的关系分析清楚,然后再根据这些关系的特点来合理的选用逻辑结构。尤其是在某些复杂的问题中,数据之间的关系相当复杂,且选用不同逻辑结构都可以解决这一问题,但选用不同逻辑结构实现的算法效率大不一样。
对于这一类问题,我们应采用怎样的标准对逻辑结构进行选择呢?
下文将探讨选择合理逻辑结构应充分考虑的两个因素。

一、 充分利用“可直接使用”的信息。
首先,我们这里所讲的“信息”,指的是元素与元素之间的关系。
对于待处理的信息,大致可分为“可直接使用”和“不可直接使用”两类。对于“可直接使用”的信息,我们使用时十分方便,只需直接拿来就可以了。而对于“不可直接使用”的这一类,我们也可以通过某些间接的方式,使之成为可以使用的信息,但其中转化的过程显然是比较浪费时间的。
由此可见,我们所需要的是尽量多的“可直接使用”的信息。这样的信息越多,算法的效率就会越高。
对于不同的逻辑结构,其包含的信息是不同的,算法对信息的利用也会出现不同的复杂程度。因此,要使算法能够充分利用“可直接使用”的信息,而避免算法在信息由“不可直接使用”向“可直接使用”的转化过程中浪费过多的时间,我们必然需要采用一种合理的逻辑结构,使其包含更多“可直接使用”的信息。
〖问题一〗 IOI99的《隐藏的码字》。
〖问题描述〗
问题中给出了一些码字和一个文本,要求编程找出文本中包含这些码字的所有项目,并将找出的项目组成一个最优的“答案”,使得答案中各项目所包含的码字长度总和最大。每一个项目包括一个码字,以及该码字在文本中的一个覆盖序列(如’abcadc’就是码字’abac’的一个覆盖序列),并且覆盖序列的长度不超过1000。同时,“答案”要求其中每个项目的覆盖序列互相没有重叠。
〖问题分析〗
对于此题,一种较容易得出的基本算法是:对覆盖序列在文本中的终止位置进行循环,再判断包含了哪些码字,找出所有项目,并最后使用动态规划的方法将项目组成最优的“答案”。
算法的其它方面我们暂且不做考虑,而先对问题所采用的逻辑结构进行选择。
如果我们采用线性的逻辑结构(如循环队列),那么我们在判断是否包含某个码字t时,所用的方法为:初始时用指针p指向终止位置,接着通过p的不断前移,依次找出码字t从尾到头的各个字母。例如码字为“ABDCAB”,而文本图1-1,终止位置为最右边的箭头符号,每个箭头代表依次找到的码字的各个字母。
指针p的移动方向
A B D C A B

C D A C B D C A D C D B A D C C B A D

图1-1

由于题目规定码字的覆盖序列长度不超过1000,所以进行这样的一次是否包含的判断,其复杂度为O(1000)。
由于码字t中相邻两字母在文本中的位置,并非只有相邻(如图1-1中的’D’和’C’)这一种关系,中间还可能间隔了许多的字母(如图1-1中’C’和’A’就间隔了2个字母),而线性结构中拥有的信息,仅仅只存在于相邻的两元素之间。通过这样简单的信息来寻找码字的某一个字母,其效率显然不高。
如果我们建立一个有向图,其中顶点i(即文本的第i位)用52条弧分别连接’a’..’z’,’A’..’Z’这52个字母在i位以前最后出现的位置(如图1-2的连接方式),我们要寻找码字中某个字母的前一个字母,就可以直接利用已连接的边,而不需用枚举的方法。我们也可以把问题看为:从有向图的一个顶点出发,寻找一条长度为length(t)-1的路径,并且路径中经过的顶点,按照码字t中的字母有序。

C D A C B D C A D C D B A D C C B A D

图1-2
通过计算,用图进行记录在空间上完全可以承受(记录1000个点×52条弧×4字节的长整型=200k左右)。在时间上,由于可以充分利用第i位和第i+1位弧的连接方式变化不大这一点(如图1-2所示,第i位和第i+1位只有一条弧的指向发生了变化,即第i+1位将其中一条弧指向了第i位),所以要对图中的弧进行记录,只需对弧的指向进行整体赋值,并改变其中的某一条弧即可。
因此,我们通过采用图的逻辑结构,使得寻找字母的效率大大提高,其判断的复杂度为O(length(t)),最坏为O(100),比原来方法的判断效率提高了10倍。
(附程序codes.pas)

对于这个例子,虽然用线性的数据结构也可以解决,但由于判断的特殊性,每次需要的信息并不能从相邻的元素中找到,而线性结构中只有相邻元素之间存在关系的这一点,就成为了一个很明显的缺点。因此,问题一线性结构中的信息,就属于“不可直接使用”的信息。相对而言,图的结构就正好满足了我们的需要,将所有可能产生关系的点都用弧连接起来,使我们可以利用弧的关系,高效地进行判断寻找的过程。虽然图的结构更加复杂,但却将“不可直接使用”的信息,转化成为了“可直接使用”的信息,算法效率的提高,自然在情理之中。。
二、 不记录“无用”信息。
从问题一中我们看到,由于图结构的信息量大,所以其中的信息基本上都是“可用”的。但是,这并不表示我们就一定要使用图的结构。在某些情况下,图结构中的“可用”信息,是有些多余的。
信息都“可用”自然是好事,但倘若其中“无用”(不需要)的信息太多,就只会增加我们思考分析和处理问题时的复杂程度,反而不利于我们解决问题了。
〖问题二〗 湖南省1997年组队赛的《乘船问题》
〖问题描述〗
有N个人需要乘船,而每船最多只能载两人,且必须同名或同姓。求最少需要多少条船。
〖问题分析〗
看到这道题,很多人都会想到图的数据结构:将N个人看作无向图的N个点,凡同名或同姓的人之间都连上边。
要满足用船最少的条件,就是需要尽量多的两人共乘一条船,表现在图中就是要用最少的边完成对所有顶点的覆盖。这就正好对应了图论的典型问题:求最小边的覆盖。所用的算法为“求任意图最大匹配”的算法。
使用“求任意图最大匹配”的算法比较复杂(要用到扩展交错树,对花的收缩等等),效率也不是很高。因此,我们必须寻找一个更简单高效的方法。
首先,由于图中任两个连通分量都是相对独立的,也就是说任一条匹配边的两顶点,都只属于同一个连通分量。因此,我们可以对每个连通分量分别进行处理,而不会影响最终的结果。
同时,我们还可以对需要船只s的下限进行估计:
对于一个包含Pi个顶点的连通分量,其最小覆盖边数显然为[Pi/2]。若图中共有L个连通分量,则s=∑[Pi/2](1<=i<=L)。
然后,我们通过多次尝试,可得出一个猜想:
实际需要的覆盖边数完全等于我们求出的下限∑[Pi/2](1<=i<=L)。
要用图的结构对上述猜想进行证明,可参照以下两步进行:
1. 连通分量中若不存在度为1的点,就必然存在回路。
2. 从图中删去度为1的点及其相邻的点,或删去回路中的任何一边,连通分量依然连通,即连通分量必然存在非桥边。
由于图的方法不是这里的重点,所以具体证明不做详述。而由采用图的数据结构得出的算法为:每次输出一条非桥的边,并从图中将边的两顶点删去。此算法的时间复杂度为O(n3)。(寻找一条非桥边的复杂度为O(n2),寻找覆盖边操作的复杂度为O(n))
由于受到图结构的限制,时间复杂度已经无法降低,所以如果我们要继续对算法进行优化,只有考虑使用另一种逻辑结构。这里,我想到了使用二叉树的结构,具体说就是将图中的连通分量都转化为二叉树,用二叉树来解决问题。
首先,我们以连通分量中任一个顶点作为树根,然后我们来确定建树的方法。
1. 找出与根结点i同姓的点j(j不在二叉树中)作为i的左儿子,再以j为树根建立子树。
2. 找出与根结点i同名的点k(k不在二叉树中)作为i的右儿子,再以k为树根建立子树。
如图2-1-1中的连通分量,我们通过上面的建树方法,可以使其成为图2-1-2中的二叉树的结构(以结点1为根)。(两点间用实线表示同姓,虚线表示同名)

图2-1-2

图2-1-1
接着,我就来证明这棵树一定包含了连通分量中的所有顶点。
【引理2.1】
若二叉树T中包含了某个结点p,那么连通分量中所有与p同姓的点一定都在T中。
证明:
为了论证的方便,我们约定:s表示与p同姓的顶点集合;lc[p,0]表示结点p,lc[p,i](i>0)表示lc[p,i-1]的左儿子,显然lc[p,i]与p是同姓的。
假设存在某个点q,满足qs且qT。由于s是有限集合,因而必然存在某个lc[p,k]无左儿子。则我们可以令lc[p,k+1]=q,所以qT,与假设qT相矛盾。
所以假设不成立,原命题得证。

由引理2.1的证明方法,我们同理可证引理2.2。
【引理2.2】
若二叉树T中包含了某个结点p,那么连通分量中所有与p同名的点一定都在T中。

有了上面的两个引理,我们就不难得出下面的定理了。
【定理一】
以连通分量中的任一点p作为根结点的二叉树,必然能够包含连通分量中的所有顶点。
证明:
由引理2.1和引理2.2,所有与p同姓或同名的点都一定在二叉树中,即连通分量中所有与p有边相连的点都在二叉树中。由连通分量中任两点间都存在路径的特性,该连通分量中的所有点都在二叉树中。

在证明二叉树中包含了连通分量的所有顶点后,我们接着就需要证明我们的猜想,也就是下面的定理:
【定理二】包含m个结点的二叉树Tm,只需要船的数量为boat[m]=[m/2](mN)。
证明:
(i) 当m=1,m=2,m=3时命题显然成立。

图2-2-1

图2-2-2

图2-2-3
(ii) 假设当m<k(k>3)时命题成立,那么当m=k时,我们首先从树中找到一个层次最深的结点,并假设这个结点的父亲为p。那么,此时有且只有以下三种情况(结点中带有阴影的是p结点):
(1) 如图2-2-1,p只有一个儿子。此时删去p和p唯一的儿子,Tk就成为了Tk-2,则boat[k]=boat[k-2]+1=[(k-2)/2]+1=[k/2]。
(2) 如图2-2-2,p有两个儿子,并且p是其父亲的左儿子。此时可删去p和p的右儿子,并可将p的左儿子放到p的位置上。同样地,Tk成为了Tk-2,boat[k]=boat[k-2]+1=[k/2]。
(3) 如图2-2-3,p有两个儿子,并且p是其父亲的右儿子。此时可删去p和p的左儿子,并可将p的右儿子放到p的位置上。情况与(2)十分相似,易得此时得boat[k]=boat[k-2]+1=[k/2]。
综合(1)、(2)、(3),当m=k时,boat[k]=[k/2]。
最后,综合(i)、(ii),对于一切mN,boat[m]=[m/2]。

由上述证明,我们将问题中数据的图结构转化为树结构后,可以得出求一棵二叉树的乘船方案的算法:
proc try(father:integer;var root:integer;var rest:byte);
{输出root为树根的子树的乘船方案,father=0表示root是其父亲的左儿子,
father=1表示root是其父亲的右儿子,rest表示输出子树的乘船方案后,
是否还剩下一个根结点未乘船}
begin
visit[root]:=true; {标记root已访问}
找到一个与root同姓且未访问的结点j;
if j<>n+1 then try(0,j,lrest);
找到一个与root同姓且未访问的结点k;
if k<>n+1 then try(1,k,rrest);
if (lrest=1) xor (rrest=1) then begin {判断root是否只有一个儿子,情况一}
if lrest=1 then print(lrest,root) else print(rrest,root);
rest:=0;
end
else if (lrest=1) and (rrest=1) then begin {判断root是否有两个儿子}
if father=0 then begin
print(rrest,root);root:=j; {情况二}
end
else begin
print(lrest,root);root:=k; {情况三}
end;
rest:=1;
end
else rest:=1;
end;

这只是输出一棵二叉树的乘船方案的算法,要输出所有人的乘船方案,我们还需再加一层循环,用于寻找各棵二叉树的根结点,但由于每个点都只会访问一次,寻找其左右儿子各需进行一次循环,所以算法的时间复杂度为O(n2)。(附程序boat.pas)

最后,我们对两种结构得出不同时间复杂度算法的原因进行分析。其中最关键的一点就是因为二叉树虽然结构相对较简单,但已经包含了几乎全部都“有用”的信息。由我们寻找乘船方案的算法可知,二叉树中的所有边不仅都发挥了作用,而且没有重复的使用,可见信息的利用率也是相当之高的。
既然采用树结构已经足够,图结构中的一些信息就显然就成为了“无用”的信息。这些多余的“无用”信息,使我们在分析问题时难于发现规律,也很难找到高效的算法进行解决。这正如迷宫中的墙一样,越多越难走。“无用”的信息,只会干扰问题的规律性,使我们更难找出解决问题的方法。

小结
我们对数据的逻辑结构进行选择,是构造数学模型一大关键,而算法又是用来解决数学模型的。要使算法效率高,首先必须选好数据的逻辑结构。上面已经提出了选择逻辑结构的两个条件(思考方向),总之目的是提高信息的利用效果。利用“可直接使用”的信息,由于中间不需其它操作,利用的效率自然很高;不不记录“无用”的信息,就会使我们更加专心地研究分析“有用”的信息,对信息的使用也必然会更加优化。
总之,在解决问题的过程中,选择合理的逻辑结构是相当重要的环
三、 选择合理的存储结构
数据的存储结构,分为顺序存储结构和链式存储结构。顺序存储结构的特点是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;链式存储结构则是借助指示元素存储地址的指针表示数据元素之间的逻辑关系。
因为两种存储结构的不同,导致这两种存储结构在具体使用时也分别存在着优点和缺点。
这里有一个较简单的例子:我们需要记录一个n×n的矩阵,矩阵中包含的非0元素为m个。
此时,我们若采用顺序存储结构,就会使用一个n×n的二维数组,将所有数据元素全部记录下来;若采用链式存储结构,则需要使用一个包含m个结点的链表,记录所有非0的m个数据元素。由这样两种不同的记录方式,我们可以通过对数据的不同操作来分析它们的优点和缺点。
1. 随机访问矩阵中任意元素。由于顺序结构在物理位置上是相邻的,所以可以很容易地获得任意元素的存储地址,其复杂度为O(1);对于链式结构,由于不具备物理位置相邻的特点,所以首先必须对整个链表进行一次遍历,寻找需进行访问的元素的存储地址,其复杂度为O(m)。此时使用顺序结构显然效率更高。
2. 对所有数据进行遍历。两种存储结构对于这种操作的复杂度是显而易见的,顺序结构的复杂度为O(n2),链式结构为O(m)。由于在一般情况下m要远小于n2,所以此时链式结构的效率要高上许多。
除上述两种操作外,对于其它的操作,这两种结构都不存在很明显的优点和缺点,如对链表进行删除或插入操作,在顺序结构中可表示为改变相应位置的数据元素。
既然两种存储结构对于不同的操作,其效率存在较大的差异,那么我们在确定存储结构时,必须仔细分析算法中操作的需要,合理地选择一种能够“扬长避短”的存储结构。

一、合理采用顺序存储结构。
我们在平常做题时,大多都是使用顺序存储结构对数据进行存储。究其原因,一方面是出于顺序结构操作方便的考虑,另一方面是在程序实现的过程中,使用顺序结构相对于链式结构更便于对程序进行调试和查找错误。因此,大多数人习惯上认为,能够使用顺序结构进行存储的问题,最“好”采用顺序存储结构。
其实,这个所谓的“好”只是一个相对的标准,是建立在以下两个前提条件之下的:
1. 链式结构存储的结点与顺序结构存储的结点数目相差不大。这种情况下,由于存储的结点数目比较接近,使用链式结构完全不能体现出记录结点少的优点,并且可能会由于指针操作较慢而降低算法的效率。更有甚者,由于指针自身占用的空间较大,且结点数目较多,因而算法对空间的要求可能根本无法得到满足。
2. 并非算法效率的瓶颈所在。由于不是算法最费时间的地方,这里是否进行改进,显然是不会对整个算法构成太大影响的,若使用链式结构反而会显得操作过于繁琐。

二、必要时采用链式存储结构。
上面我对使用顺序存储结构的条件进行了分析,最后就只剩下何时应该采用链式存储结构的问题了。
由于链式结构中指针操作确实较繁琐,并且速度也较慢,调试也不方便,因而大家一般都不太愿意用链式的存储结构。但是,这只是一般的观点,当链式结构确实对算法有很大改进时,我们还是不得不进行考虑的。
〖问题三〗 IOI99的《地下城市》。
〖问题描述〗
已知一个城市的地图,但未给出你的初始位置。你需要通过一系列的移动和探索,以确定初始时所在的位置。题目的限制是:
1. 不能移动到有墙的方格。
2. 只能探索当前所在位置四个方向上的相邻方格。
在这两个限制条件下,要求我们的探索次数(不包括移动)尽可能的少。
〖问题分析〗
由于存储结构要由算法的需要确定,因此我们首先来确定问题的算法。
经过对问题的分析,我们得出解题的基本思想:先假设所有无墙的方格都可能是初始位置,再通过探索一步步地缩小初始位置的范围,最终得到真正的初始位置。同时,为提高算法效率,我们还用到了分治的思想,使我们每一次探索都尽量多的缩小初始位置的范围(使程序尽量减少对运气的依赖)。
接着,我们来确定此题的存储结构。
由于这道题的地图是一个二维的矩阵,所以一般来讲,采用顺序存储结构理所当然。但是,顺序存储结构在这道题中暴露了很大的缺点。我们所进行的最多的操作,一是对初始位置的范围进行筛选,二是判断要选择哪个位置进行探索。而这两种操作,所需要用到的数据,只是庞大地图中很少的一部分。如果采用顺序存储结构(如图3-1中阴影部分表示已标记),无论你需要用到多少数据,始终都要完全的遍历整个地图。

4
3
2
1
1 2 3 4
图3-1

head

图3-2
然而,如果我们采用的是链式存储结构(如图3-2的链表),那么我们需要多少数据,就只会遍历多少数据,这样不仅充分发挥了链式存储结构的优点,而且由于不需单独对某一个数据进行提取,每次都是对所有数据进行判断,从而避免了链式结构的最大缺点。
我们使用链式存储结构,虽然没有降低问题的时间复杂度(链式存储结构在最坏情况下的存储量与顺序存储结构的存储量几乎相同),但由于体现了前文所述选择存储结构时扬长避短的原则,因而算法的效率也大为提高。(程序对不同数据的运行时间见表3-3)
测试数据编号 使用顺序存储结构的程序 使用链式存储结构的程序
1 0.06s 0.02s
2 1.73s 0.07s
3 1.14s 0.06s
4 3.86s 0.14s
5 32.84s 0.21s
6 141.16s 0.23s
7 0.91s 0.12s
8 6.92s 0.29s
9 6.10s 0.23s
10 17.41s 0.20s

表3-3
(附使用链式存储结构的程序under.pas)
我们选择链式的存储结构,虽然操作上可能稍复杂一些,但由于改进了算法的瓶颈,算法的效率自然也今非昔比。由此可见,必要时选择链式结构这一方法,其效果是不容忽视的。
小结
合理选择逻辑结构,由于牵涉建立数学模型的问题,可能大家都会比较注意。但是对存储结构的选择,由于不会对算法复杂度构成影响,所以比较容易忽视。那么,这种不能降低算法复杂度的方法是否需要重视呢?
大家都知道,剪枝作为一种常用的优化算法的方法,被广泛地使用,但剪枝同样是无法改变算法的复杂度的。因此,作用与剪枝相似的存储结构的合理选择,也是同样很值得重视的。
总之,我们在设计算法的过程中,必须充分考虑存储结构所带来的不同影响,选择最合理的存储结构。

四、 多种数据结构相结合

上文所探讨的,都是如何对数据结构进行选择,其中包含了逻辑结构的选择和存储结构的选择,是一种具有较大普遍性的算法优化方法。对于多数的问题,我们都可以通过选择一种合理的逻辑结构和存储结构以达到优化算法的目的。
但是,有些问题却往往不如人愿,要对这类问题的数据结构进行选择,常常会顾此失彼,有时甚至根本就不存在某一种合适的数据结构。此时,我们是无法选择出某一种合适的数据结构的,以上的方法就有些不太适用了。
为解决数据结构难以选择的问题,我们可以采用将多种数据结构进行结合的方法。通过多种数据结构相结合,达到取长补短的作用,使不同的数据结构在算法中发挥出各自的优势。
这只是我们将多种数据结构进行结合的总思想,具体如何进行结合,我们可以先看下面的例子。
我们可以采用映射的方法,将线性结构中的元素与堆中间的结点一一对应起来,若线性的数组中的元素发生变化,堆中相应的结点也接着变化,堆中的结点发生变化,数组中相应的元素也跟着变化。
将两种结构进行结合后,无论是第一步还是第二步,我们都不需对所有元素进行遍历,只需进行常数次复杂度为O(log2n)的堆化操作。这样,整个时间复杂度就成为了O(nlog2n),算法效率无疑得到了很大提高。

五、 总结
我们平常使用数据结构,往往只将其作为建立模型和算法实现的工具,而没有考虑这种工具对程序效率所产生的影响。信息学问题随着难度的不断增大,对算法时空效率的要求也越来越高,而算法的时空效率,在很大程度上都受到了数据结构的制约。

D. 二分图最大匹配Matlab程序(在线等,感激不尽!)

第一个问题比较简单,这里懒得对着你的图片敲数据,用随机数代替

long=100;

A=0.1*rand(long,1);

B=0.15*rand(long,1);

[AA BB]=meshgrid(A,B);

gg=0.5017*AA-0.65*BB;

g=gg>=-0.03&gg<=0.03; %这就是gij矩阵,相当于二分图的连接矩阵

[num h] = maxnum(g);%第二个问题就是求二分图的最大匹配问题,这里

%调用了一个自己写maxnum函数,返回num就是最大值,h是hij(不唯一)


以下是maxnum.m的内容,用的是匈牙利算法

其中还用了一个递归的incpath函数,寻找增广路径

function[numh]=maxnum(g)
s=size(g);
globalG_h;%矩阵hij记录选中
globalG_g;%矩阵gij记录匹配
globalG_v;%记录当前一次路径访问过的节点
G_h=false(s);%矩阵hij初始为空
G_g=g;%矩阵gij就是传递进来的参数g
fori=1:s(1)
G_v=false(1,s(2));%每次初始化径访问过的节点为空
incpath(i);%从Ai开始寻找增广路径
end
h=G_h;num=sum(h(:));%输出最大匹配数,和匹配矩阵h
clearglobal'G_h';clearglobal'G_g';
end

functionOK=incpath(i)%从Ai开始
globalG_h;globalG_g;globalG_v;OK=false;
j=find(~G_h(i,:)&G_g(i,:)&~G_v,1);%寻找合条件的Bj
ifisempty(j),return;end%找不到返回false
G_v(j)=true;%找到了,标记Bj为以访问节点
ii=find(G_h(:,j));%寻找Bj在原来匹配中
ifisempty(ii)%如果不在原匹配中
G_h(i,j)=true;OK=true;return;end%找到增广路径末端,返回true
ok=incpath(ii);%如果在原来的匹配中,根据匹配对应的Aii递归调用incpath寻找
ifok%如果递归寻找返回成功
G_h(i,j)=~G_h(i,j);G_h(ii,j)=~G_h(ii,j);OK=true;return;end%路径反色返回true
end

E. 二分图最大匹配的Hopcroft-Carp算法的pascal实现,求各神牛,悬赏!!!!

program hopcroft_karp;
const maxn=100;
var linkx, linky, distx, disty, queue: array[1..maxn] of longint; //link表示已经匹配的边 x为左 y为右
map: array[1..maxn, 1..maxn] of boolean;
n, m, i, x, y, ans: longint;
function bfs: boolean;
var i, now, head, tail: longint;
begin
fillchar(distx,sizeof(distx),0); //dist表示该点在增广路中被搜到时的距离
fillchar(disty,sizeof(disty),0);
bfs:=false;
head:=0; tail:=0;
for i:=1 to n do if (linkx[i]=0) then begin
inc(tail);
queue[tail]:=i;
end;
while (head<tail) do begin //利用队列搜索增广路
inc(head); now:=queue[head];
for i:=1 to n do if map[now,i] and (disty[i]=0) then begin //disty[i]<>0: 增广路不能交叉
disty[i]:=distx[now]+1;
if (linky[i]=0) then bfs:=true
else begin //左边的点无须判断dist是否为0
distx[linky[i]]:=disty[i]+1;
inc(tail);
queue[tail]:=linky[i]; //queue只记录左边的点
end;
end;
end;
end;
function dfs(x:longint): boolean;
var i: longint;
begin
for i:=1 to n do if map[x,i] and (disty[i]=distx[x]+1) then begin //为了判断是否能形成增广路
disty[i]:=0; //dist改为0 防止下次再搜到此点 由于这个条件可能导致增广路转道
if (linky[i]=0) or dfs(linky[i]) then begin //有时候会使原来搜索到的一些增广路无法继续更新
linkx[x]:=i; //但是不会出现错误
linky[i]:=x; //DFS过程类似hungry算法
exit(true);
end;
end;
exit(false);
end;
begin
fillchar(map,sizeof(map),false);
read(n,m);
for i:=1 to m do begin
read(x,y);
map[x,y]:=true; //map不能赋双向值
end;
fillchar(linkx,sizeof(linkx),0);
fillchar(linky,sizeof(linky),0);
ans:=0;
while bfs do
for i:=1 to n do
if (linkx[i]=0) and dfs(i) then inc(ans);
writeln(ans);
end.
思路:首先用bfs判断图中是否有增广路,如果有,则利用dist记录下来,然后用dfs处理增广路,不断循环直到不存在增广路
由于bfs一次可以判断出许多增广路,算法会比hungry快很多,hungry的复杂度为n*m,而hopcroft的复杂度为n^0.5*m

F. 匈牙利法中直线覆盖选择的最小值

匈牙利法中直线覆盖选择的最小值:二分图最大匹配数=最小点覆盖率。

二分图的最小点覆盖的理解:找到最少的一些点,使二分图所有的边都至少有一个端点在这些点之中。倒过来说就是,删除包含这些点的边,可以删掉所有边。

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择。最大独立数:选取最多的点,使任意所选两点均不相连。

最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

匈牙利算法

匈牙利算法(Hungarian algorithm),其核心就是寻找增广路径,是一种用增广路径求二分图最大匹配的算法。匈牙利算法是一种在P问题内(多项式时间内)求解任务分配问题的组合优化算法。它推动了后来的原始对偶方法。

匈牙利算法是美国数学家哈罗德·库恩于1955年提出的。此算法之所以被称作匈牙利算法,是因为算法很大一部分是基于以前匈牙利数学家Dénes Kőnig和Jenő Egerváry的工作之上创建起来的。

G. 什么是二分图的匹配,最大匹配,带权最大匹配

给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
求二分图最大匹配可以用最大流或者匈牙利算法。

H. 求二分图最大匹配的Matlab程序

[numh]=maxnum(g);%g是二分图邻接矩阵
%调用了一个自己写maxnum函数,返回num就是最大值,h是hij(不唯一)

以下是maxnum.m的内容,用的是匈牙利算法
其中还用了一个递归的incpath函数,寻找增广路径

function[numh]=maxnum(g)
s=size(g);
globalG_h;%矩阵hij记录选中
globalG_g;%矩阵gij记录匹配
globalG_v;%记录当前一次路径访问过的节点
G_h=false(s);%矩阵hij初始为空
G_g=g;%矩阵gij就是传递进来的参数g
fori=1:s(1)
G_v=false(1,s(2));%每次初始化径访问过的节点为空
incpath(i);%从Ai开始寻找增广路径
end
h=G_h;num=sum(h(:));%输出最大匹配数,和匹配矩阵h
clearglobal'G_h';clearglobal'G_g';
end

functionOK=incpath(i)%从Ai开始
globalG_h;globalG_g;globalG_v;OK=false;
j=find(~G_h(i,:)&G_g(i,:)&~G_v,1);%寻找合条件的Bj
ifisempty(j),return;end%找不到返回false
G_v(j)=true;%找到了,标记Bj为以访问节点
ii=find(G_h(:,j));%寻找Bj在原来匹配中
ifisempty(ii)%如果不在原匹配中
G_h(i,j)=true;OK=true;return;end%找到增广路径末端,返回true
ok=incpath(ii);%如果在原来的匹配中,根据匹配对应的Aii递归调用incpath寻找
ifok%如果递归寻找返回成功
G_h(i,j)=~G_h(i,j);G_h(ii,j)=~G_h(ii,j);OK=true;return;end%路径反色返回true
end

I. 离散数学的图论中的二部图的完全匹配和最大匹配问题怎么理解

11个互不同构的生成子图,18个互不同构的子图
ps:生成子图按边数考虑,边数从0到6,子图按顶点数考虑

J. 程序员必须掌握哪些算法

一.基本算法:

枚举. (poj1753,poj2965)

贪心(poj1328,poj2109,poj2586)

递归和分治法.

递推.

构造法.(poj3295)

模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996)

二.图算法:

图的深度优先遍历和广度优先遍历.

最短路径算法(dijkstra,bellman-ford,floyd,heap+dijkstra)
(poj1860,poj3259,poj1062,poj2253,poj1125,poj2240)
最小生成树算法(prim,kruskal)
(poj1789,poj2485,poj1258,poj3026)
拓扑排序 (poj1094)

二分图的最大匹配 (匈牙利算法) (poj3041,poj3020)

最大流的增广路算法(KM算法). (poj1459,poj3436)

三.数据结构.

串 (poj1035,poj3080,poj1936)

排序(快排、归并排(与逆序数有关)、堆排) (poj2388,poj2299)

简单并查集的应用.

哈希表和二分查找等高效查找法(数的Hash,串的Hash)
(poj3349,poj3274,POJ2151,poj1840,poj2002,poj2503)
哈夫曼树(poj3253)



trie树(静态建树、动态建树) (poj2513)

四.简单搜索

深度优先搜索 (poj2488,poj3083,poj3009,poj1321,poj2251)

广度优先搜索(poj3278,poj1426,poj3126,poj3087.poj3414)

简单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)

五.动态规划

背包问题. (poj1837,poj1276)

型如下表的简单DP(可参考lrj的书 page149):
E[j]=opt{D+w(i,j)} (poj3267,poj1836,poj1260,poj2533)
E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij} (最长公共子序列) (poj3176,poj1080,poj1159)
C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题)
六.数学

组合数学:
1.加法原理和乘法原理.
2.排列组合.
3.递推关系.
(POJ3252,poj1850,poj1019,poj1942)
数论.
1.素数与整除问题
2.进制位.
3.同余模运算.
(poj2635, poj3292,poj1845,poj2115)
计算方法.
1.二分法求解单调函数相关知识.(poj3273,poj3258,poj1905,poj3122)
七.计算几何学.

几何公式.

叉积和点积的运用(如线段相交的判定,点到线段的距离等). (poj2031,poj1039)

多边型的简单算法(求面积)和相关判定(点在多边型内,多边型是否相交)
(poj1408,poj1584)
凸包. (poj2187,poj1113)

中级(校赛压轴及省赛中等难度):
一.基本算法:

C++的标准模版库的应用. (poj3096,poj3007)

较为复杂的模拟题的训练(poj3393,poj1472,poj3371,poj1027,poj2706)

二.图算法:

差分约束系统的建立和求解. (poj1201,poj2983)

最小费用最大流(poj2516,poj2516,poj2195)

双连通分量(poj2942)

强连通分支及其缩点.(poj2186)

图的割边和割点(poj3352)

最小割模型、网络流规约(poj3308)

三.数据结构.

线段树. (poj2528,poj2828,poj2777,poj2886,poj2750)

静态二叉检索树. (poj2482,poj2352)

树状树组(poj1195,poj3321)

RMQ. (poj3264,poj3368)

并查集的高级应用. (poj1703,2492)

KMP算法. (poj1961,poj2406)

四.搜索

最优化剪枝和可行性剪枝

搜索的技巧和优化 (poj3411,poj1724)

记忆化搜索(poj3373,poj1691)

五.动态规划

较为复杂的动态规划(如动态规划解特别的旅行商TSP问题等)
(poj1191,poj1054,poj3280,poj2029,poj2948,poj1925,poj3034)
记录状态的动态规划. (POJ3254,poj2411,poj1185)

树型动态规划(poj2057,poj1947,poj2486,poj3140)

六.数学

组合数学:
1.容斥原理.
2.抽屉原理.
3.置换群与Polya定理(poj1286,poj2409,poj3270,poj1026).
4.递推关系和母函数.
数学.
1.高斯消元法(poj2947,poj1487, poj2065,poj1166,poj1222)
2.概率问题. (poj3071,poj3440)
3.GCD、扩展的欧几里德(中国剩余定理) (poj3101)
计算方法.
1.0/1分数规划. (poj2976)
2.三分法求解单峰(单谷)的极值.
3.矩阵法(poj3150,poj3422,poj3070)
4.迭代逼近(poj3301)
随机化算法(poj3318,poj2454)
杂题(poj1870,poj3296,poj3286,poj1095)
七.计算几何学.

坐标离散化.

扫描线算法(例如求矩形的面积和周长并,常和线段树或堆一起使用)
(poj1765,poj1177,poj1151,poj3277,poj2280,poj3004)
多边形的内核(半平面交)(poj3130,poj3335)

几何工具的综合应用.(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429)

高级(regional中等难度):
一.基本算法要求:

代码快速写成,精简但不失风格

(poj2525,poj1684,poj1421,poj1048,poj2050,poj3306)

保证正确性和高效性. poj3434

二.图算法:

度限制最小生成树和第K最短路. (poj1639)

最短路,最小生成树,二分图,最大流问题的相关理论(主要是模型建立和求解)
(poj3155, poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446
最优比率生成树. (poj2728)

最小树形图(poj3164)

次小生成树.

无向图、有向图的最小环

三.数据结构.

trie图的建立和应用. (poj2778)

LCA和RMQ问题(LCA(最近公共祖先问题) 有离线算法(并查集+dfs) 和 在线算法(RMQ+dfs)).(poj1330)
双端队列和它的应用(维护一个单调的队列,常常在动态规划中起到优化状态转移的目的). (poj2823)
左偏树(可合并堆).

后缀树(非常有用的数据结构,也是赛区考题的热点).(poj3415,poj3294)
四.搜索

较麻烦的搜索题目训练(poj1069,poj3322,poj1475,poj1924,poj2049,poj3426)

广搜的状态优化:利用M进制数存储状态、转化为串用hash表判重、按位压缩存储状态、双向广搜、A*算法. (poj1768,poj1184,poj1872,poj1324,poj2046,poj1482)

深搜的优化:尽量用位运算、一定要加剪枝、函数参数尽可能少、层数不易过大、可以考虑双向搜索或者是轮换搜索、IDA*算法. (poj3131,poj2870,poj2286)

五.动态规划

需要用数据结构优化的动态规划.(poj2754,poj3378,poj3017)
四边形不等式理论.

较难的状态DP(poj3133)

六.数学

组合数学.
1.MoBius反演(poj2888,poj2154)
2.偏序关系理论.
博奕论.
1.极大极小过程(poj3317,poj1085)
2.Nim问题.
七.计算几何学.

半平面求交(poj3384,poj2540)

可视图的建立(poj2966)

点集最小圆覆盖.

对踵点(poj2079)

热点内容
安卓qq邀请码在哪里寻找 发布:2025-05-15 00:02:04 浏览:33
三菱fx编程口 发布:2025-05-15 00:01:23 浏览:809
医院招商引资宣传片脚本 发布:2025-05-15 00:01:21 浏览:367
linuxcftp服务器 发布:2025-05-14 23:58:18 浏览:717
探岳什么配置才有驾驶模式选择 发布:2025-05-14 23:53:17 浏览:144
如何在手机上看无限流量密码 发布:2025-05-14 23:43:31 浏览:114
19投篮脚本 发布:2025-05-14 23:36:57 浏览:513
编译器怎么处理c变长数组 发布:2025-05-14 23:31:46 浏览:663
存折每天可以输错多少次密码 发布:2025-05-14 23:22:06 浏览:909
安卓手机怎么找微信隐藏对话 发布:2025-05-14 23:07:47 浏览:338