四 03
匈牙利算法
链接:
USACO 4.2.2 The Perfect Stall 完美的牛栏 stall4
这是一种用增广路求二分图最大匹配的算法。它由匈牙利数学家Edmonds于1965年提出,因而得名。 定义 未盖点:设Vi是图G的一个顶点,如果Vi 不与任意一条属于匹配M的边相关联,就称Vi 是一个未盖点。

交错路:设P是图G的一条路,如果P的任意两条相邻的边一定是一条属于M而另一条不属于M,就称P是一条交错路。
可增广路:两个端点都是未盖点的交错路叫做可增广路。 

流程图

伪代码:
bool 寻找从k出发的对应项出的可增广路 { while (从邻接表中列举k能关联到顶点j) { if (j不在增广路上) { 把j加入增广路; if (j是未盖点 或者 从j的对应项出发有可增广路) { 修改j的对应项为k; 则从k的对应项出有可增广路,返回true; } } } 则从k的对应项出没有可增广路,返回false; } void 匈牙利hungary() { for i->1 to n { if (则从i的对应项出有可增广路) 匹配数++; } 输出 匹配数; } |
演示























C实现(作者BYVoid)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #include <stdio.h> #include <string.h> #define MAX 102 long n,n1,match; long adjl[MAX][MAX]; long mat[MAX]; bool used[MAX]; FILE *fi,*fo; void readfile() { fi=fopen("flyer.in","r"); fo=fopen("flyer.out","w"); fscanf(fi,"%ld%ld",&n,&n1); long a,b; while (fscanf(fi,"%ld%ld",&a,&b)!=EOF) adjl[a][ ++adjl[a][0] ]=b; match=0; } bool crosspath(long k) { for (long i=1;i<=adjl[k][0];i++) { long j=adjl[k][i]; if (!used[j]) { used[j]=true; if (mat[j]==0 || crosspath(mat[j])) { mat[j]=k; return true; } } } return false; } void hungary() { for (long i=1;i<=n1;i++) { if (crosspath(i)) match++; memset(used,0,sizeof(used)); } } void print() { fprintf(fo,"%ld",match); fclose(fi); fclose(fo); } int main() { readfile(); hungary(); print(); return 0; } |
Pascal实现(作者魂牛)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | var a:array[1..1000,1..1000] of boolean; b:array[1..1000] of longint; c:array[1..1000] of boolean; n,k,i,x,y,ans,m:longint; function path(x:longint):boolean; var i:longint; begin for i:=1 to n do if a[x,i] and not c[i] then begin c[i]:=true; if (b[i]=0) or path(b[i]) then begin b[i]:=x; exit(true); end; end; exit(false); end; procedure hungary; var i:longint; begin fillchar(b,sizeof(b),0); for i:=1 to m do begin fillchar(c,sizeof(c),0); if path(i) then inc(ans); end; end; begin fillchar(a,sizeof(a),0); readln(m,n,k); for i:=1 to k do begin readln(x,y); a[x,y]:=true; end; ans:=0; hungary; writeln(ans); end. |
鸣谢:魂牛
一点废话
在魂牛的帮助下,我终于正确得写出了了匈牙利算法,原来它这么好写啊。感谢魂牛支持!
另外,hungary这个词容易让我联想到hungry。。。饿了,我要去吃加餐了。
总算写完了,画图真累啊。。。。。。
求最大匹配用匈牙利算法原来这么简单,我以前一直是用网络最大流求最大匹配的
[回复]
弱弱的问一句……
魂牛的那个Pascal版本的path过程有递归..也就意味着有回溯…那么假如选择边的时候没选好….会不会造成重复搜索…导致复杂度边高…
[回复]
所以我建议改成非递归的BFS……
我是OIBH的日月双蚀…..
大牛可以短信M偶…
[回复]
谢谢你啊
希望你能告诉我BFS算法
[回复]
六月 17th, 2008 at 15:34:36
[...] 最简单的求二分图最大匹配,最朴素的匈牙利算法解决。 [...]
没看懂
[回复]
关于匈牙利算法我现在只记得它解决关于二分图的匹配问题的= =
[回复]
哪里没看懂啊?我觉得很详细了
[回复]
真谢谢了,可我真的不懂,望大牛能将所有的图论算法发到我邮箱,感谢不尽。
[回复]
什么叫做饱和?
另外你的流程图, 不是程序的流程把?
按我理解, 第一条边应该是x1,y1
[回复]
BYVoid 回复:
十月 5th, 2009 at 02:03:07
下面的演示并没有按照程序流程图
[回复]
谢谢神牛的讲解,解决了我长期的疑问
[回复]
有始以来看到的最详细的一次流程分析,很透彻,很有用。很好,继续加油。
[回复]