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

转自: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的模板给出:

int pred[maxn] , mk[maxn] , open[maxn] ;
int MaxMatch()
{
        int i , 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
        return res ;
}
 
原文地址:https://www.cnblogs.com/crazyapple/p/2647721.html