图的匹配——匈牙利算法与KM算法
- 格式:doc
- 大小:267.50 KB
- 文档页数:20
前言:高中时候老师讲这个就听得迷迷糊糊,有一晚花了通宵看KM的Pascal代码,大概知道过程了,后来老师说不是重点,所以忘的差不多了。
都知道二分图匹配是个难点,我这周花了些时间研究了一下这两个算法,总结一下1.基本概念M代表匹配集合未盖点:不与任何一条属于M的边相连的点交错轨:属于M的边与不属于M的边交替出现的轨(链)可增广轨:两端点是未盖点的交错轨判断M是最大匹配的标准:M中不存在可增广轨2.最大匹配,匈牙利算法时间复杂度:O(|V||E|)原理:寻找M的可增广轨P,P包含2k+1条边,其中k条属于M,k+1条不属于M。
修改M 为M&P。
即这条轨进行与M进行对称差分运算。
所谓对称差分运算,就是比如X和Y都是集合,X&Y=(X并Y)-(x交Y)有一个定理是:M&P的边数是|M|+1,因此对称差分运算扩大了M实现:关于这个实现,有DFS和BFS两种方法。
先列出DFS的代码,带注释。
这段代码来自中山大学的教材核心部分在dfs(x),来寻找可增广轨。
如果找到的话,在Hungarian()中,最大匹配数加一。
这是用了刚才提到的定理。
大家可以想想初始状态是什么,又是如何变化的view plaincopy to clipboardprint?第二种方法BFS,来自我的学长cnhawk核心步骤还是寻找可增广链,过程是:1.从左的一个未匹配点开始,把所有她相连的点加入队列2.如果在右边找到一个未匹配点,则找到可增广链3.如果在右边找到的是一个匹配的点,则看它是从左边哪个点匹配而来的,将那个点出发的所有右边点加入队列这么说还是不容易明白,看代码吧view plaincopy to clipboardprint?3.最佳匹配加权图中,权值最大的最大匹配KM算法:概念:f(v)是每个点的一个值,使得对任意u,v C V,f(u)+f(v)>=w[e u,v]集合H:一个边集,使得H中所有u,v满足f(u)+f(v)=w[e u,v]等价子图:G f(V,H),标有f函数的G图理论:对于f和G f,如果有一个理想匹配集合M p,则M p最优。
二分图的最优匹配(KM算法)KM算法用来解决最大权匹配问题:在一个二分图内,左顶点为X,右顶点为Y,现对于每组左右连接XiYj有权wij,求一种匹配使得所有wij的和最大。
基本原理该算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。
设顶点Xi的顶标为A[ i ],顶点Yj的顶标为B[ j ],顶点Xi与Yj之间的边权为w[i,j]。
在算法执行过程中的任一时刻,对于任一条边(i,j),A[ i ]+B[j]>=w[i,j]始终成立。
KM算法的正确性基于以下定理:若由二分图中所有满足A[ i ]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。
首先解释下什么是完备匹配,所谓的完备匹配就是在二部图中,X点集中的所有点都有对应的匹配或者是Y点集中所有的点都有对应的匹配,则称该匹配为完备匹配。
这个定理是显然的。
因为对于二分图的任意一个匹配,如果它包含于相等子图,那么它的边权和等于所有顶点的顶标和;如果它有的边不包含于相等子图,那么它的边权和小于所有顶点的顶标和。
所以相等子图的完备匹配一定是二分图的最大权匹配。
初始时为了使A[ i ]+B[j]>=w[i,j]恒成立,令A[ i ]为所有与顶点Xi关联的边的最大权,B[j]=0。
如果当前的相等子图没有完备匹配,就按下面的方法修改顶标以使扩大相等子图,直到相等子图具有完备匹配为止。
我们求当前相等子图的完备匹配失败了,是因为对于某个X顶点,我们找不到一条从它出发的交错路。
这时我们获得了一棵交错树,它的叶子结点全部是X顶点。
现在我们把交错树中X顶点的顶标全都减小某个值d,Y顶点的顶标全都增加同一个值d,那么我们会发现:1)两端都在交错树中的边(i,j),A[ i ]+B[j]的值没有变化。
也就是说,它原来属于相等子图,现在仍属于相等子图。
2)两端都不在交错树中的边(i,j),A[ i ]和B[j]都没有变化。
⼆分图匹配--匈⽛利算法⼆分图匹配--匈⽛利算法⼆分图匹配匈⽛利算法基本定义:⼆分图 —— 对于⽆向图G=(V,E),如果存在⼀个划分使V中的顶点分为两个互不相交的⼦集,且每个⼦集中任意两点间不存在边 ϵ∈E,则称图G为⼀个⼆分图。
⼆分图的充要条件是,G⾄少有两个顶点,且所有回路长度为偶数。
匹配 —— 边的集合,其中任意两条边都不存在公共顶点。
匹配边即是匹配中的元素,匹配点是匹配边的顶点,同样⾮匹配边,⾮匹配点相反定义。
最⼤匹配——在图的所有匹配中,包含最多边的匹配成为最⼤匹配 完美匹配——如果在⼀个匹配中所有的点都是匹配点,那么该匹配称为完美匹配。
附注:所有的完美匹配都是最⼤匹配,最⼤匹配不⼀定是完美匹配。
假设完美匹配不是最⼤匹配,那么最⼤匹配⼀定存在不属于完美匹配中的边,⽽图的所有顶点都在完美匹配中,不可能找到更多的边,所以假设不成⽴,及完美匹配⼀定是最⼤匹配。
交替路——从⼀个未匹配点出发,依次经过⾮匹配边,匹配边,⾮匹配边…形成的路径称为交替路,交替路不会形成环。
增⼴路——起点和终点都是未匹配点的交替路。
因为交替路是⾮匹配边、匹配边交替出现的,⽽增⼴路两端节点都是⾮匹配点,所以增⼴路⼀定有奇数条边。
⽽且增⼴路中的节点(除去两端节点)都是匹配点,所属的匹配边都在增⼴路径上,没有其他相连的匹配边,因此如果把增⼴路径中的匹配边和⾮匹配边的“⾝份”交换,就可以获得⼀个更⼤的匹配(该过程称为改进匹配)。
⽰例图Fig1_09_09.JPG注释:Fig3是⼀个⼆分图G=(V,E),V={1,2,3,4,5,6,7,8},E={(1,7),(1,5),(2,6),(3,5),(3,8),(4,5),(4,6)},该图可以重绘成Fig4,V可分成两个⼦集V={V1,V2},V1={1,2,3,4},V2={5,6,7,8}。
Fig4中的红⾊边集合就是⼀个匹配{(1,5),(4,6),(3,8)}Fig2中是最⼤匹配Fig1中红⾊边集合是完美匹配Fig1中交替路举例(4-6-2-7-1-5)Fig4中增⼴路(2-6-4-5-1-7)匈⽛利树匈⽛利树中从根节点到叶节点的路径均是交替路,且匈⽛利树的叶节点都是匹配点。
求kM算法和匈牙利算法的程序代码kM算法和匈牙利算法的程序代码,最好是用matlab给出的,用c语言亦可。
不要用其他的编程语言。
//二分图最佳匹配,kuhn munkras算法,邻接阵形式,复杂度O(m*m*n)//返回最佳匹配值,传入二分图大小m,n和邻接阵mat,表示权值//match1,match2返回一个最佳匹配,未匹配顶点match值为-1//一定注意m<=n,否则循环无法终止//最小权匹配可将权值取相反数#include <string.h>#define MAXN 310#define inf 1000000000#define _clr(x) memset(x,0xff,sizeof(int)*n)int kuhn_munkras(int m,int n,int mat[][MAXN],int* match1,int* match2){ int s[MAXN],t[MAXN],l1[MAXN],l2[MAXN],p,q,ret=0,i,j,k;for (i=0;i<m;i++)for (l1[i]=-inf,j=0;j<n;j++)l1[i]=mat[i][j]>l1[i]?mat[i][j]:l1[i];for (i=0;i<n;l2[i++]=0);for (_clr(match1),_clr(match2),i=0;i<m;i++){for (_clr(t),s[p=q=0]=i;p<=q&&match1[i]<0;p++)for (k=s[p],j=0;j<n&&match1[i]<0;j++)if (l1[k]+l2[j]==mat[k][j]&&t[j]<0){s[++q]=match2[j],t[j]=k;if (s[q]<0)for (p=j;p>=0;j=p)match2[j]=k=t[j],p=match1[k],match1[k]=j;}if (match1[i]<0){for (i--,p=inf,k=0;k<=q;k++)for (j=0;j<n;j++)if (t[j]<0&&l1[s[k]]+l2[j]-mat[s[k]][j]<p)p=l1[s[k]]+l2[j]-mat[s[k]][j];for (j=0;j<n;l2[j]+=t[j]<0?0:p,j++);for (k=0;k<=q;l1[s[k++]]-=p);}}for (i=0;i<m;i++)ret+=mat[i][match1[i]];return ret;}昨天帮一个同学完成了他的毕业论文上的指派问题的匈牙利算法程序。
二分图最优匹配:对于二分图的每条边都有一个权(非负),要求一种完备匹配方案,使得所有匹配边的权和最大,记做最优完备匹配。
(特殊的,当所有边的权为1时,就是最大完备匹配问题)解二分图最优匹配问题可用穷举的方法,但穷举的效率=n!,所以我们需要更加优秀的算法。
先说一个定理:设M是一个带权完全二分图G的一个完备匹配,给每个顶点一个可行顶标(第i个x顶点的可行标用lx[i]表示,第j个y顶点的可行标用ly[j]表示),如果对所有的边(i,j) in G,都有lx[i]+ly[j]>=w[i,j]成立(w[i,j]表示边的权),且对所有的边(i,j) in M,都有lx[i]+ly[j]=w[i,j]成立,则M是图G的一个最优匹配。
Kuhn-Munkras算法(即KM算法)流程:v(1)初始化可行顶标的值v(2)用匈牙利算法寻找完备匹配v(3)若未找到完备匹配则修改可行顶标的值v(4)重复(2)(3)直到找到相等子图的完备匹配为止KM算法主要就是控制怎样修改可行顶标的策略使得最终可以达到一个完美匹配,首先任意设置可行顶标(如每个X节点的可行顶标设为它出发的所有弧的最大权,Y节点的可行顶标设为0),然后在相等子图中寻找增广路,找到增广路就沿着增广路增广。
而如果没有找到增广路呢,那么就考虑所有现在在匈牙利树中的X节点(记为S集合),所有现在在匈牙利树中的Y节点(记为T 集合),考察所有一段在S集合,一段在not T集合中的弧,取delta = min {l(xi)+l(yj)-w(xi,yj) , | xi in S, yj in not T} 。
明显的,当我们把所有S集合中的l(xi)减少delta之后,一定会有至少一条属于(S, not T)的边进入相等子图,进而可以继续扩展匈牙利树,为了保证原来属于(S,T )的边不退出相等子图,把所有在T 集合中的点的可行顶标增加delta。
随后匈牙利树继续扩展,如果新加入匈牙利树的Y节点是未盖点,那么找到增广路,否则把该节点的对应的X匹配点加入匈牙利树继续尝试增广。
匈⽛利算法(⼆分图)---------------------------------------------------------------------题材⼤多来⾃⽹络,本篇由神犇整理基本概念—⼆分图⼆分图:是图论中的⼀种特殊模型。
若能将⽆向图G=(V,E)的顶点V划分为两个交集为空的顶点集,并且任意边的两个端点都分属于两个集合,则称图G为⼀个为⼆分图。
匹配:⼀个匹配即⼀个包含若⼲条边的集合,且其中任意两条边没有公共端点。
如下图,图3的红边即为图2的⼀个匹配。
1 最⼤匹配在G的⼀个⼦图M中,M的边集中的任意两条边都不依附于同⼀个顶点,则称M是⼀个匹配。
选择这样的边数最⼤的⼦集称为图的最⼤匹配问题,最⼤匹配的边数称为最⼤匹配数.如果⼀个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
如果在左右两边加上源汇点后,图G等价于⼀个⽹络流,最⼤匹配问题可以转为最⼤流的问题。
解决此问的匈⽛利算法的本质就是寻找最⼤流的增⼴路径。
上图中的最⼤匹配如下图红边所⽰:2 最优匹配最优匹配⼜称为带权最⼤匹配,是指在带有权值边的⼆分图中,求⼀个匹配使得匹配边上的权值和最⼤。
⼀般X和Y集合顶点个数相同,最优匹配也是⼀个完备匹配,即每个顶点都被匹配。
如果个数不相等,可以通过补点加0边实现转化。
⼀般使⽤KM算法解决该问题。
3 最⼩覆盖⼆分图的最⼩覆盖分为最⼩顶点覆盖和最⼩路径覆盖:①最⼩顶点覆盖是指最少的顶点数使得⼆分图G中的每条边都⾄少与其中⼀个点相关联,⼆分图的最⼩顶点覆盖数=⼆分图的最⼤匹配数;②最⼩路径覆盖也称为最⼩边覆盖,是指⽤尽量少的不相交简单路径覆盖⼆分图中的所有顶点。
⼆分图的最⼩路径覆盖数=|V|-⼆分图的最⼤匹配数;4 最⼤独⽴集最⼤独⽴集是指寻找⼀个点集,使得其中任意两点在图中⽆对应边。
对于⼀般图来说,最⼤独⽴集是⼀个NP完全问题,对于⼆分图来说最⼤独⽴集=|V|-⼆分图的最⼤匹配数。
【原创】我的KM算法详解0.二分图二分图的概念二分图又称作二部图,是图论中的一种特殊模型。
设G=(V, E)是一个无向图。
如果顶点集V可分割为两个互不相交的子集X和Y,并且图中每条边连接的两个顶点一个在X中,另一个在Y中,则称图G为二分图。
可以得到线上的driver与order之间的匹配关系既是一个二分图。
二分图的判定无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。
判断无向连通图是不是二分图,可以使用深度优先遍历算法(又名交叉染色法)。
下面着重介绍下交叉染色法的定义与原理首先任意取出一个顶点进行染色,和该节点相邻的点有三种情况:1.如果节点没有染过色,就染上与它相反的颜色,推入队列,2.如果节点染过色且相反,忽视掉,3.如果节点染过色且与父节点相同,证明不是二分图,return二分图博客推荐交叉染色法博客推荐:交叉染色法判断二分图另外附上二分图的性质博客:二分图的一些性质1.KM算法初步KM算法全称是Kuhn-Munkras,是这两个人在1957年提出的,有趣的是,匈牙利算法是在1965年提出的。
?增广路径增广路径定义:若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M 的一条增广路径(举例来说,有A、B集合,增广路由A中一个点通向B中一个点,再由B中这个点通向A中一个点……交替进行)增广路径有如下特性:?1. 有奇数条边?2. 起点在二分图的X边,终点在二分图的Y边?3. 路径上的点一定是一个在X边,一个在Y边,交错出现。
?4. 整条路径上没有重复的点?5. 起点和终点都是目前还没有配对的点,其他的点都已经出现在匹配子图中?6. 路径上的所有第奇数条边都是目前还没有进入目前的匹配子图的边,而所有第偶数条边都已经进入目前的匹配子图。
奇数边比偶数边多一条边?7. 于是当我们把所有第奇数条边都加到匹配子图并把条偶数条边都删除,匹配数增加了1.例如下图,蓝色的是当前的匹配子图,红色表示未匹配的路径,目前只有边x0y0,然后通过x1找到了增广路径:x1y0-y0x0-x0y2?增广路径有两种寻径方法,一个是深搜,一个是宽搜。
KM算法详解(例题为HDU2255 带权二分图的最优匹配):希望你在阅读此篇博客时,你已经学会了匈牙利算法,这样会对你更有帮助哦!!!!!对于KM算法自己的通俗理解与代码详解:注:KM算法:就是在匈牙利基础上加了权值的束缚!那么,为了达到权值和最大,或者最小,就不能简单的去算最多的边数。
步骤:(以HDU2255 例题为例)1.首先要找到所有居民愿意花钱最多的那个房子。
题目中用到lx,ly数组,是为了同时调节两个数组,使得权值和最大。
或者说当要松弛的时候使得本来最大的矛盾权值和尽可能的损失小一些来得到满足条件的最大权值和!2.(lx[x]+ly[y]-w[x][y]=0)条件下进行匈牙利算法。
lx[x]+ly[y]-w[x][y]这个条件十分巧妙:(算法精髓)1.即可以只让指定居民找到它愿意付最多钱的房子。
2.又可以在发生多居民抢一个房子时,用它来得到该居民到其它房子的松弛量!(即该居民到其它房子比到这个用钱最多的房子愿意花的钱数上差的值。
)那么我们就要把发生矛盾的居民到其它房子的松弛量的最小值求出来。
再用它去松弛,就可以让原本矛盾的最大权值和,损失最小而得到满足条件的最大权值和对于每个居民有4个基本问题:1.这个房子访问过没有?2.这个房子能不能满足他的条件3.这个房子是否被别人住了4.被别人住了能不能得到调配代码如下:#include iostream#includecstdio#includecstringusing namespace std;#define MAX 310#define INF 125#define clr(x) memset(x,0,sizeof(x))int w[MAX][MAX];int lx[MAX],ly[MAX]; -***lx[i]初始化为A集合中 i 点能到B集合某一点的最大权值, ly[i] 初始化0;***-int link[MAX];int slack[MAX];int visx[MAX],visy[MAX];bool find(int x)visx[x]=1; -****得到发生矛盾的居民集合****-for(int y=1;y=n;y++) -**这个居民,每个房子都去试一试!(找到就退出)**-if(visy[y]) -****一个房子不需要重复访问****-continue;int t=lx[x]+ly[y]-w[x][y];-****按这个标准去用-匈牙利算法***- if(t==0) -**t==0标志这个房子可以给这位居民**-visy[y]=1;if(link[y]==0||find(link[y])) -****这房子没人住或可以让住这个房子的人去找另外的房子住****-link[y]=x;return true; -**那么就可以让这位居民住进来**-else if(slack[y]t) -**否则这个房子不能给这位居民!**-slack[y]=t; -***就要找到这个房子要松弛多少才能够给这位居民***--***且当有多个居民都对这个房子有松弛量时,要找到最小的。
图的匹配一、什么是图的匹配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 的一个匹配。
定义∑∈=me 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,当且仅当工人xi 胜任工作yi时,G中有一条边xiyi,于是人员分配问题就成为在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;constmaxn = 100;varmap: array[1 .. maxn, 1 .. maxn] of boolean; {保存二分图} cover: array[1 .. maxn] of boolean; {标记一个点是否为饱和点} link: array[1 .. maxn] of integer; {保存匹配边}n: integer; {顶点数}procedure init; {读入}vari, e, x, y: integer;beginassign(input, 'input.txt'); reset(input);readln(n, e);for i := 1 to e do beginreadln(x, y); map[x, y] := true;end;close(input);end;function find(i: integer): boolean; {从i出发,找增广链}vark, q: integer;beginfind := true;for k := 1 to n doif map[i, k] and not cover[k] then beginq := 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; {求匹配}vari: integer;beginfor i := 1 to n do beginfillchar(cover, sizeof(cover), 0);find(i);end;end;procedure print; {输出}vari, s: integer; beginassign(output, 'output.txt'); rewrite(output);s := 0; for i := 1 to n do if link[i] <> 0 then s := s + 1; writeln(s);for i := 1 to n do if link[i] <> 0 then writeln(link[i], ' ', i); close(output); end;begin init; main; print; end.2.二分图的最大权匹配对于上面的人员分配问题,如果还考虑到工人做工的效率,就可以提出所谓的分派问题:应该怎样分配才能使总的效率最大?同上一节,我们可以构造一个二分图G ,如果把工人x i 做工作y i 的效率w ij 看作是G 中边x i y i 的权,则分派问题就相当于在赋权二分图G 中求一个最大全匹配。
由线性规划的知识,求二分图G 的最大权匹配,只需在匈牙利算法的基础上少许改进即可。
它的基本思想是,对二分图的顶点编号,然后根据编号构造一个新的二分图G ’,最后把求G 的最大权匹配转换为求G ’的完美匹配。
下面的这条定理是这个算法的理论基础。
定理:设M 是赋权图(权非负)的完全二分图),,,(w E Y X G =的一个完美匹配,这里Y X =。
如果存在一组},{j i μλ,满足),,2,1,(,n j i w ij j i =≥+μλ,并且对一切M y x j i ∈,均有ij j i w =+μλ,则M 是G 的最大权匹配。
下面,给出求最大权匹配的程序。
输入文件中首先是N 和|E|,下面|E|行每行三个数(I, J, W),表示工人I 做工作J 的效率是W 。
程序输出包括每个工人的选择和最后的总效益。
其它假设参见上一节的算法假设。
这个算法的时间复杂度也是O(n 3)。
Program maxmatch; constmaxn = 100; varmap: array[1 .. maxn, 1 .. maxn] of integer; {保存二分图}link, lx, ly: array[1 .. maxn] of integer; {保存匹配边和每个点的标号} x, y: array[1 .. maxn] of boolean; {标记一个点是否为饱和点} n: integer; {顶点数}procedure init; {读入} vari, e, x, y: integer; beginassign(input, 'input.txt'); reset(input);for i := 1 to e do readln(x, y, map[x, y]);close(input);end;function find(v: integer): boolean; {从v出发,找增广链} vari, k: integer;beginfind := true;x[v] := true;for i := 1 to n doif not y[i] and (lx[v] + ly[i] = map[v, i]) then beginy[i] := true; k := link[i]; link[i] := v;if (k = 0) or find(k) then exit;link[i] := k;end;find := false;end;procedure main; {求最大权匹配}vari, j, k, d: integer;beginfillchar(lx, sizeof(lx), 0); fillchar(ly, sizeof(ly), 0);for i := 1 to n dofor j := 1 to n doif map[i, j] > lx[i] then lx[i] := map[i, j];for k := 1 to n dorepeatfillchar(x, sizeof(x), 0); fillchar(y, sizeof(y), 0);if find(k) then break;d := maxint;for i := 1 to n do if x[i] thenfor j := 1 to n do if not y[j] thenif lx[i] + ly[j] - map[i, j] < d thend := lx[i] + ly[j] - map[i, j];for i := 1 to n do if x[i] then lx[i] := lx[i] - d;for i := 1 to n do if y[i] then ly[i] := ly[i] + d;until false;end;procedure print; {输出}vari, s: integer;beginassign(output, 'output.txt'); rewrite(output);s := 0;for i := 1 to n do s := s + map[link[i], i];close(output);end;begininit;main;print;end.3. 任意图的匹配任意图的最大匹配算法也是建立在找增广链的基础上的,只是任意图的增广链要比二分图难找得多。