KM算法讲解
对于二分图,我们可以用匈牙利来求出来最大匹配,但是如果给定每条边一个权值,我们要求这张图的最大匹配最大(小)权,单纯的用匈牙利就没法解决了,当然用费用流也可以做,但是代码较长,在处理完全二分图的时候时间也较长。
我们这时引入一个新的算法,就是KM。
对于KM算法,我们引入顶标概念,规定每个点都有顶标,且左面的点(二分图的左右)的顶标设成X[I],右面的设成Y[I],w[i,j]代表i-->j这条边的权值(我们这里
求最大权值和),那么满足对于任意边,x[i]+y[j]>=w[i,j],那么,我们最后肯定可以找到一种匹配方式(完备匹配),使得这种方式中的每一条边都满足x[i]+y[j]=w[i,j]那么这种匹配方式一定是该二分图的最大权值匹配,因为x[i]+y[j]>=w[i,j],我们取到了w[i,j]的上限,所以是最大权值的。
那么我们给x[i],y[j]设初值的时候,因为要满足对于所有边x[i]+y[j]>=w[i,j],所以我们将x[i]设成max(w[i,k])将y[i]设成0,这样就满足了。
那么我们也需要一个点一个点的匹配,类似于匈牙利,就是对于满足x[i]+y[j]=w[i,j]的边才算一组匹配,这样我们就得到了一组可行解。
function match(i:longint):boolean; var j :longint; begin fx[i]:=true; for j:=1 to m do if (w[i,j]=tx[i]+ty[j]) and (not fy[j]) then begin fy[j]:=true; if (link[j]=0) or (match(link[j])) then begin link[j]:=i; exit(true); end; end; exit(false); end;
当前的可行解可以构成一张图,包含2*m个点和m个边(交错树),但是当前的可行解不一定是最终的解,可能最后答案的一些边不在当前集合中,这时我们需要更新顶标,使得更多的点可以来更新答案。
那么我们设一个c,使当前所有在交错树中的X[i]减去c,使y[i]加上c,那么我们可以得到一些性质:
procedure update; var d :longint; i, j :longint; c :longint; begin c:=maxlongint; for i:=1 to n do if fx[i] then for j:=1 to m do if not fy[j] then c:=min(c,tx[i]+ty[j]-w[i,j]); for i:=1 to n do if fx[i] then dec(tx[i],c); for i:=1 to m do if fy[i] then inc(ty[i],c); end;
那么这样一来到最后我们就可以得到这张图的最大权值匹配了,最小只需取相反数即可
附主程序
procedure main; var i, j :longint; begin for i:=1 to n do for j:=1 to m do tx[i]:=max(tx[i],w[i,j]); for i:=1 to n do while true do begin fillchar(fx,sizeof(fx),false); fillchar(fy,sizeof(fy),false); if match(i) then break else update; end; for j:=1 to m do ans:=ans+w[link[j],j]; writeln(ans); end;