二分图最大匹配 pascal

二分图最大匹配。

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

 

先给出一些定义:

二分图:        设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]of boolean;//记录读入的边
 mark:array[1..1000+1]of boolean;//标记在一个点是否在增广路中
 link:array[1..1000+1]of longint;//不是记录增广路,而是记录匹配边

 

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) then exit(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 n do//这里枚举的是点,但进入find函数后,枚举的还是以i点为端点的边。就是要把i点加入增广路中
  begin
  fillchar(mark,sizeof(mark),false);
  if find(i) then inc(s);//s是最大匹配数,这里的意思是:从i出发的一条边在增广路中,所以s自然要+1
  end;
 writeln(s);
end;

begin
 init;
 main;
end.

 

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

 

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值