图的匹配——匈牙利算法与KM算法

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

图的匹配

一、什么是图的匹配

1.图的定义

无向图:无向图G 是指非空有限集合V G ,和V G 中某些元素的无序对的集合E G ,构成的二元组(V G ,E G )。V G 称为G 的顶点集,其中的元素称为G 的顶点。E G 称为G 的边集,其中的元素称为G 的边。在不混淆的情况下,有时记V =V G ,E =E G 。如果V ={v 1,…,v n },那么E 中的元素e 与V 中某两个元素构成的无序对(v i ,v j )相对应,记e =v i v j ,或e =v j v i 。在分析问题时,我们通常可以用小圆圈表示顶点,用小圆圈之的连线表示边。

二分图:设G 是一个图。如果存在V G 的一个划分X ,Y ,使得G 的任何一条边的一个端点在X 中,另一个端点在Y 中,则称G 为二分图,记作G =(X ,Y ,E)。如果G 中X 的每个顶点都与Y 的每个顶点相邻,则称G 为完全二分图。

2.匹配的相关概念

设G =(V ,E)是一个图,E M ⊆,如果M 不含环且任意两边都不相邻,则称M 为G 的一个匹配。G 中边数最多的匹配称为G 的最大匹配。

对于图G =(V ,E),在每条边e 上赋一个实数权w(e)。设M 是G 的一个匹配。定义∑∈=m

e e w M w )()(,并称之为匹配M 的权。G 中权最大的匹配称为G 的最大权匹配。如果

对一切,e ∈E ,w(e)=1,则G 的最大权匹配就是G 的最大匹配。

设M 是图G=(V ,E)的一个匹配,v i ∈V 。若v i 与M 中的边相关联,则称v i 是M 饱和点,否则称v i 为M 非饱和点。

如果G 中每个顶点都是M 饱和点,则称M 为G 的完美匹配。

设M 是G 的一个匹配,P 是G 的一条链。如果P 的边交替地一条是M 中的边,一条不是M 中的边,则称P 为M 交错链。类似地,我们可以定义G 的交错圈。易知,G 的交错圈一定是偶圈。

一条连接两个不同的M 非饱和点的M 交错链称为M 增广链。

两个集合S 1与S 2的“异或”操作S 1⊕S 2是指集合S 1⊕S 2=(S 1∩S 2)\(S 1∪S 2) 容易看出,设M 是G 的匹配,P 是G 中的M 增广链、则M ⊕P 也是G 的匹配,而且1+=⊕M P M 。

图表 1 “异或”操作

可以证明,G 中匹配M 是最大匹配当且仅当G 中没有M 增广链。

二、匹配算法

1.二分图的最大匹配

设有M个工人x1, x2, …, x m,和N项工作y1, y2, …, y n,规定每个工人至多做一项工作,而每项工作至多分配一名工人去做。由于种种原因,每个工人只能胜任其中的一项或几项工作。问应怎样分配才能使尽可能多的工人分配到他胜任的工作。这个问题称为人员分配问题。

人员分配问题可以用图的语言来表述。令X={x1, x2, …, x m},Y={y1, y2, …,y n},构造二分图G=(X, Y, E)如下:

对于1≤i≤m,1≤j≤n,当且仅当工人x

i 胜任工作y

i

时,G中有一条边x

i

y

i

,于是人

员分配问题就成为在G中求一个最大匹配的问题。

求最大匹配常用匈牙利算法,它的基本思想是:对于已知的匹配M,从X中的任一选定的M非饱和点出发,用标号法寻找M增广链。如果找到M增广链,则M就可以得到增广;否则从X中另一个M非饱和点出发,继续寻找M增广链。重复这个过程直到G中不存在增广链结束,此时的匹配就是G的最大匹配。这个算法通常称为匈牙利算法,因为这里介绍的寻找增广链的标号方法是由匈牙科学者Egerváry最早提出来的。

图表2 匈牙利算法

理解了这个算法,就不难写出人员分配问题的解答了。在给出程序之前,先做一些假设:

为了简单起见,假设工人数等于工作数,即N=M,且N≤100,这里,N也可以看作是二分图的|X|和|Y|。

数据从文件input.txt中读入,首先是N和|E|,下面|E|行每行两个数(I, J),表示工人I 可以胜任工作J,即二分图中的边x i y j。

结果输出到文件output.txt,第一行是最大匹配数s,下面s行每行两个数(I, J),表示分配工人I做工作J,即匹配边x i y j。

下面是人员分配问题的参考解答,该算法的时间复杂度为O(n3)。

Program match;

const

maxn = 100;

var

map: array[1 .. maxn, 1 .. maxn] of boolean; {保存二分图} cover: array[1 .. maxn] of boolean; {标记一个点是否为饱和点} link: array[1 .. maxn] of integer; {保存匹配边}

n: integer; {顶点数}

procedure init; {读入}

var

i, e, x, y: integer;

begin

assign(input, 'input.txt'); reset(input);

readln(n, e);

for i := 1 to e do begin

readln(x, y); map[x, y] := true;

end;

close(input);

end;

function find(i: integer): boolean; {从i出发,找增广链}

var

k, q: integer;

begin

find := true;

for k := 1 to n do

if map[i, k] and not cover[k] then begin

q := link[k]; link[k] := i; cover[k] := true;

if (q = 0) or find(q) then exit;

link[k] := q;

end;

find := false;

end;

procedure main; {求匹配}

var

i: integer;

begin

for i := 1 to n do begin

fillchar(cover, sizeof(cover), 0);

find(i);

end;

end;

procedure print; {输出}

var

相关文档
最新文档