浅谈算法_二分图匹配
定义
二分图
简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集U和V,使得每一条边都分别连接U,V中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图 1 是一个二分图。
匹配
在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。
算法大致流程
有一个图:
每一条线都代表一条边,每连一条边都表示一次匹配,现在我们想让左边匹配到的点最多。
那么,我们考虑,先给第一个点连边,用贪心的策略,使能匹配到的点最多。
首先:
我们用左边的第一个点连向了右边的第一个点(左边第一个点的第一条边)。
接着开始匹配第二个点:
发现与第一个点重合了,就往回找第一个点有没有其他连边,发现第一个点与右边第而个点有连边,于是我们更改它的连边(递归回去):
发现两个都可以,便继续往下做:
又发现有边已经连了,再次回去更改连边:
发现又被连了,便再回去改第二个点的匹配:
发现没有被匹配到,皆大欢喜。
继续做最后一个点,只有一条边,连上就好了:
至此匹配过程完成!
更有趣的讲解:
http://blog.csdn.net/dark_scope/article/details/8880547
主要代码
link:array[0..10000]of longint;
mark:array[0..10000]of boolean;
map:array[0..100,0..1000]of boolean;
i,s,n,m,t,x,y:longint;
function find(t:longint):boolean;
var
i,q:longint;
begin
for i:=1 to m do
if (not mark[i])and(map[t,i]) then
begin
q:=link[i];
link[i]:=t;
mark[i]:=true;
if (q=0)or(find(q)) then
exit(true);
link[i]:=q;
end;
exit(false);
end;
begin
readln(n,m,t);
for i:=1 to t do
begin
readln(x,y);
map[x,y]:=true;
end;
fillchar(link,sizeof(link),0);
s:=0;
for i:=1 to n do
begin
fillchar(mark,sizeof(mark),0);
if find(i) then
inc(s);
end;
writeln(s);
end.
习题
有兴趣的可以做做这几题:
【东莞市选2008】导弹:http://172.16.0.132/senior/#main/show/1015
【NOIP2013模拟联考3】沙耶的玩偶(doll):http://172.16.0.132/senior/#contest/show/2100/2