zoukankan      html  css  js  c++  java
  • 最大匹配之匈牙利算法模板。。

    最大匹配之匈牙利算法模板。。

    转自:http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2135405.html

    要学习匈牙利算法先要懂得二部图的各种概念。。下面给出由o(∩_∩)o MiYu总结的一般性概念,这些概念很重要,一定要懂。。

    二分图的基本概念:( 意思就是所有的点分成了2个集合x, y. 每个集合中的顶点相互间没有边)

    一个无向图G = < V, E >, 如果存在两个集合X, Y, 使得X∪Y=V, X∩Y=Φ, 并且每一条边e={x, y}

    有x∈X,y∈Y, 则称G为一个二分图(bipartite graph). 常用来表示一个二分图. 若对X中任一x及Y中任一y

    恰有一边e∈E, 使e = {x, y}, 则称G为完全二分图(complete bipartite graph).

    二分图的性质:( 交错轨 和增广路的概念很重要)

    定理:无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数.

    匹配:设G=为二分图,如果M⊆E,并且M中没有任何两边有公共端点。M=Φ时称M为空匹配.

    盖点: 若M是二分图的一个匹配, 将M中的边多所关联的顶点称为盖点, 其余则为 未盖点.

    交错轨: 若一条路径上属于M的边和不属于M 的边交替出现, 则称该路径为交错轨.

    增广路径: 若 路径P 是一条起始点和 终点都是未盖点的交错轨, 那么P 称为M 的增广路径.

    最大匹配: G的所有匹配中边数最多的匹配称为最大匹配.

    性质1: 一条关于M的增广路径的长度必为奇数, 且路上的第一条边和最后一条边都不属于M.
    性质2: 对于一条关于M的增广路径P, 将M 中属于P的边删去, 将P中不属于M 的边添加到M中,
    所得到的边集合计为M ΘP, 则M ΘP 比M 多一条匹配边.
    性质3 :M 为G 的一个最大匹配当且仅当不存在关于M的增广路径.
    hall 定理: 对于二分图G = ( X, Y, E ) , 存在一个匹配M, 使得X 的所有顶点关于M饱和
    的充要条件是: 对X 的任一子集A , 对A邻接的点集为P (A), 恒有: | P[A] | >= | A |
    其中 性质2 和性质3 和hall 定理的充分性证明 就是 匈牙利算法的基础…..

    二分图的 最小顶点覆盖==== 最大匹配

    DAG图的 最小路径覆盖数== 节点数 – 最大匹配数

    二分图的 最大独立集数= 节点数 – 最大匹配数

    在二分图中求最少的点,让每条边都至少和其中的一个点关联,这就是
    二分图的“最小顶点覆盖”。

    最小路径覆盖:

    一个PXP的有向图中,路径覆盖就是在图中找一些路经,使之覆

    盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;(

    如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以

    经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每

    每条路径就是一个弱连通子集.

      由上面可以得出:

      1.一个单独的顶点是一条路径;

      2.如果存在一路径p1,p2,……pk,其中p1 为起点,pk为终点,那

    么在覆盖图中,顶点p1,p2,……pk不再与其它的顶点之间存在有向边.

      最小路径覆盖就是找出最小的路径条数,使之成为P的一个路径覆盖.

      路径覆盖与二分图匹配的关系(必须是没有圈的有向图):

      最小路径覆盖=|P|-最大匹配数;

      其中最大匹配数的求法是把P中的每个顶点pi分成两个顶点pi’与pi”,

    如果在p中存在一条pi到pj的边,那么在二分图P'中就有一条连接pi’与

    pj”的无向边;这里pi’ 就是p中pi的出边,pj”就是p中pj 的一条入边;

      对于公式:最小路径覆盖=|P|-最大匹配数;可以这么来理解;

      如果匹配数为零,那么P中不存在有向边,于是显然有:

      最小路径覆盖=|P|-最大匹配数=|P|-0=|P|;即P

    的最小路径覆盖数为|P|;

      P'中不在于匹配边时,路径覆盖数为|P|;

      如果在P'中增加一条匹配边pi’-->pj”,那么在图P的路径覆盖

    中就存在一条由pi连接pj的边,也就是说pi与pj 在一条路径上,于是路

    径覆盖数就可以减少一个;

      如此继续增加匹配边,每增加一条,路径覆盖数就减少一条;直到

    匹配边不能继续增加时,路径覆盖数也不能再减少了,此时就有了前面

    的公式;但是这里只 是说明了每条匹配边对应于路径覆盖中的一条路径

    上的一条连接两个点之间的有向边;下面来说明一个路径覆盖中的每条

    连接两个顶点之间的有向边对应于一条匹配 边;

      与前面类似,对于路径覆盖中的每条连接两个顶点之间的每条有向

    边pi—>pj,我们可以在匹配图中对应做一条连接pi’与pj”的边, 显然这

    样做出来图的是一个匹配图(这一点用反证法很容易证明,如果得到的图

    不是一个匹配图,那么这个图中必定存在这样两条边pi’—pj” 及pi’ —-pk”,

    (j!=k),那么在路径覆盖图中就存在了两条边pi–>pj, pi—>pk ,那边从

    pi出发的路径就不止一条了,这与路径覆盖图是矛盾的;还有另外一种情况就

    是存在pi’—pj”,pk’—pj”,这种情况也类似可证);

      至此,就说明了匹配边与路径覆盖图中连接两顶点之间边的一一对应关系,

    那么也就说明了前面的公式成立!

    匈牙利算法的原理为:从当前匹配(如果没有匹配,则初始匹配为0)出发,检查每一个未盖点,然后从它出发寻找可增广路,找到可增广路,则沿着这条可增广路进行扩充,直到不存在可增广路为止。 
    根据从未盖点出发寻找可增广路搜索的方法,可以分为: 
    1) DFS 增广 
    2) BFS增广 
    3) 多增广路(Hopcroft-Karp算法

    然后是匈牙利的模板:

    结合HDU 1240 Asteroids来给出模板:

    题意就是把给出点连起来,就是求最小点覆盖

    #include <iostream>
    #include <math.h>
    using namespace std;

    #define MAX 502
    int map[MAX][MAX];
    int n,k;
    int mk[MAX];
    //从X集合中的顶点u出发用深度优先的策略寻找增广路
    //(这种增广路只能使当前的匹配数增加1)
    int nx,ny; //X和Y集合中顶点的个数
    int cx[MAX],cy[MAX];
    //cx[i]表示最终求得的最大匹配中与Xi匹配的Y顶点, cy[i]同理
    int path(int u)
    {
     for(int v=1; v<=ny; v++) //考虑所有Yi顶点v
     {
      if(map[u][v]&&!mk[v])
      {
       mk[v]=1;
       //如果v没有匹配,或者如果v已经匹配了,
       //但从y[v]出发可以找到一条增广路
       if(cy[v]==-1|| path(cy[v]))
       {
        cx[u] = v; //把v匹配给u
        cy[v] = u; //把u匹配给v
        return 1; //找到可增广路
       }
      }
     }
     return 0 ; //如果不存在从u出发的增广路
    }
    int MaxMatch() //求二部图最大匹配的匈牙利算法
    {
     int res=0;
     memset(cx,0xff,sizeof(cx)); //从0匹配开始增广
     memset(cy,0xff,sizeof(cy));
     for(int i=1; i<=nx; i++)
     {
      if(cx[i]==-1) //从每个未盖点出发进行寻找增广路
      {
       memset(mk,0,sizeof(mk));
       res+=path(i); //每找到一条增广路,可使得匹配数加1
      }
     }
     return res;
    }
    int main()
    {
     int i,j;
     int a,b;
     while(cin>>n>>k)
     {
      nx=n;ny=n;
      memset(map,0,sizeof(map));
      for(i=0;i<k;i++)
      {
       cin>>a>>b;
       map[a][b]=1;
      }
      int max=MaxMatch();
      cout<<max<<endl;
     }
     return 0;
    }

    下面把BFS的模板给出:

    intpred[maxn] , mk[maxn] , open[maxn] ; 
    intMaxMatch() 
            inti , u , v , t , d , e , cur , tail , res(0); 
            memset(mk , 0xff , sizeof(mk)) ; 
            memset(cx , 0xff , sizeof(cx)) ; 
            memset(cy , 0xff , sizeof(cy)) ; 
            for(i = 0 ; i < nx ; i++) 
            { 
                    pred[i] = -1 ; 
                    for(open[cur = tail = 0] = i ; cur <= tail && cx[i] == -1 ; cur++) 
                    { 
                            for(u = open[cur] , v = 0 ; v < ny && cx[i] == -1 ; v ++) 
                            { 
                                    if(g[u][v] && mk[v] != i) 
                                    { 
                                            mk[v] = i ; 
                                            open[++tail] = cy[v] ; 
                                            if(open[tail] >= 0) 
                                            { pred[open[tail]] = u ; continue; } 
                                            for(d = u , e = v ; d != -1 ; 
                                                   t = cx[d], cx[d] = e, cy[e] = d, e = t, d = pred[d]); 
                        } 
                    } 
                } 
                if(cx[i] != -1) res++ ; 
            }//end of for 
            returnres ; 
  • 相关阅读:
    偶遇this之坑
    程序员的职业素养——读后感
    我怎么没想到——读后感
    黑客与画家——读后感
    漫谈认证与授权
    动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
    动手造轮子:实现简单的 EventQueue
    asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
    SQL Server 中 `JSON_MODIFY` 的使用
    WeihanLi.Npoi 近期更新
  • 原文地址:https://www.cnblogs.com/crazyapple/p/2999418.html
Copyright © 2011-2022 走看看