二分图最大匹配 pascal

来源:互联网 发布:淘宝手机营销中心 编辑:程序博客网 时间:2024/05/17 18:13

二分图最大匹配。

去年这时候学过,学得很模糊,今年重温。

 

先给出一些定义:

二分图:       设G是一个图。如果存在VG的一个划分X,Y,使得G的任何一条边的一个端点在X中,另一个端点在Y中,则称G为二分图,记作G=(X,Y,E)。

 

二分图匹配:   给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

 

二分图最大匹配:在图G的所有二分图匹配中包含边数最多的一个匹配就是二分图最大匹配。

 

增广路:      若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。

 

 

找二分图的最大匹配,最朴素的算法自然是dfs枚举。这当然会超时的。

所以就要用高级的算法,于是有了匈牙利算法。

个人觉得本质上还是枚举。只是因为引入了“增广路”的概念,所以枚举快了很多。

 

算法轮廓:

(1)置匹配M为空

(2)找出一条增广路径P,通过取反操作获得更大的匹配M’代替M

(3)重复(2)操作直到找不出增广路径为止

 

上述第二步是关键。为了达到“通过取反操作获得更大的匹配M’代替M”,只能通过枚举边来扩充增广路。用边来扩充增广路,自然有3种情况。

1)边的两个端点在这条增广路上。那么这条边肯定无法扩充增广路。

2)边的没有端点在这条增广路上。那么直接把这条边加到增广路中,增广路长度+2。

3)边的一个端点在这条增广路上。这个就比较麻烦了,可能整个增广路都要发生改变要通过递归来求解。

 

在程序实践时,又有了不同。程序中并没有很明显的“增广路”,更没有取反。

var
 n:longint;
 s:longint;
 map:array[1..1000+1,1..1000+1]ofboolean;//记录读入的边
 mark:array[1..1000+1]ofboolean;//标记在一个点是否在增广路中
 link:array[1..1000+1]oflongint;//不是记录增广路,而是记录匹配边

 

procedure init;
var
 i,x,y,e:longint;
begin
 fillchar(map,sizeof(map),0);
 read(n,e);
 for i:=1 to e do
  begin
   read(x,y);
  map[x,y]:=true;//邻接表储存
  end;
end;

 

function find(x:longint):boolean;
var
 i,q:longint;
begin
 for i:=1 to n do
  if map[x,i] and not mark[i]then//枚举边。同时排除第一种情况。
   begin
   q:=link[i]; link[i]:=x;mark[i]:=true;//改变增广路
  if (q=0) or find(q) thenexit(true);//若是第2种情况,那么第一次递归时q=0。若是第3种情况,则增广路发生改变,为了判断这种改变是有用的还是没用的,就要find(q)。因为只有增广路两端的点的link才=0,所以只要找到q=0,就表示把枚举的边加入增广路成功。
   link[i]:=q;
   end;
 exit(false);//表示找不到合适的边,该增广路不合理。
end;

 

procedure main;
var
 i:longint;
begin
 s:=0;
 for i:=1 to ndo//这里枚举的是点,但进入find函数后,枚举的还是以i点为端点的边。就是要把i点加入增广路中
  begin
  fillchar(mark,sizeof(mark),false);
  if find(i) theninc(s);//s是最大匹配数,这里的意思是:从i出发的一条边在增广路中,所以s自然要+1
  end;
 writeln(s);
end;

begin
 init;
 main;
end.

 

上面只是我的一些个人简介,不一定对。

 

对于匈牙利算法,考得时候难点主要在建图,算法本身还是背下来比较好。

因为本来代码也不是很长,如果考试时现推得不偿失。

0 0
原创粉丝点击