• 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,那么我们可以得到一些性质:

    1.两端都在交错树中的边(i,j),X[i]+Y[j]的值没有变化。也就是说,它原来属于交错树,现在仍属于交错树。
    2.两端都不在交错树中的边(i,j),X[i]和Y[j]都没有变化。也就是说,它原来属于(或不属于),现在仍属于(或不属于)交错树。
    3.X端不在交错树中,Y端在交错树中的边(i,j),它的X[i]+Y[j]的值有所增大。它原来不属于交错树,现在仍不属于交错树。
    4.X端在交错树中,Y端不在交错树中的边(i,j),它的X[i]+Y[j]的值有所减小。也就说,它原来不属于交错树,现在可能进入了交错树,因而可以更新了答案。
    那么我们要使至少一条边进入答案,这时我们的c的取值就是min(x[i]+y[j]-w[i,j]),i在交错树中,j不在交错树中,所以我们得到了更新顶标的过程
    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;
  • 相关阅读:
    HDU5914
    HDU1087(dp)
    HDU1711(KMP)
    HDU1251(字典树)
    HDU3068(Manacher算法)
    POJ2187(旋转卡壳)
    HDU1392(凸包)
    CodeForces 722B
    CodeForces 722A
    CodeForces 721B
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3479119.html
Copyright © 2020-2023  润新知