匈牙利算法

来源:互联网 发布:java泛型的通配符 编辑:程序博客网 时间:2024/06/06 10:39
原文地址:NGUNAUJ的博客
二分图:
二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。如下图所示
匈牙利算法--求二分图最大匹配 - 风未定 - NGUNAUJ
无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。因此树为二分图。
具体证明参考http://baike.baidu.com/link?url=d2W-kqfBDewABYZs02BEwdrn4EaUzswbKmBTGh782ZKosIIqDhL6PBjAus-uE33Bph-BuZJ51NQtps08pJc0pK
hall 定理 : 对于二分图 G = ( X, Y, E ) , 存在一个匹配M, 使得 X 的所有顶点关于M饱和 的充要条件是: 对 X 的任一子集A , 对A邻接的点集为P (A), 恒有 : | P[A] | >= | A |
匈牙利算法:
给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配
  v 选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
  v 如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。 最大匹配在实际中有广泛的用处,求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的复杂度为边数的指数级函数。因此,需要寻求一种更加高效的算法。 匈牙利算法是求解最大匹配的有效算法,该算法用到了增广路的定义(也称增广轨或交错轨):若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。 由增广路径的定义可以推出下述三个结论: 
v 1. P的路径长度必定为奇数,第一条边和最后一条边都不属于M。 
v 2. P经过取反操作(即非M中的边变为M中的边,原来M中的边去掉)可以得到一个更大的匹配M’。 
v 3. M为G的最大匹配当且仅当不存在相对于M的增广路径。 
从而可以得到求解最大匹配的匈牙利算法:
(1)置M为空
(2)找出一条增广路径P,通过取反操作获得更大的匹配M’代替M 
(3)重复(2)操作直到找不出增广路径为止 根据该算法,可以用dfs (深度优先搜索)实现。
这里用邻接表存储图伪代码如下:(感谢BYVoid大牛)
bool 寻找从k出发的对应项出的可增广路{    while (从邻接表中列举k能关联到顶点j)    {        if (j不在增广路上)        {            j加入增广路;            if (j是未盖点 或者 j的对应项出发有可增广路)            {                修改j的对应项为k;                则从k的对应项出有可增广路,返回true;            }        }    }    则从k的对应项出没有可增广路,返回false;}void 匈牙利hungary(){    for i->1 to n    {        if (则从i的对应项出有可增广路)            匹配数++;    }    输出 匹配数;}

vector<int>g[maxn];int match[maxn];//存储集合m中的节点i在集合n中的匹配节点,初值为-1bool vis[maxn];int tot,n;bool dfs(int x){for(int i=0;i<g[x].size();i++)if(!vis[g[x][i]]){vis[g[x][i]]=true;if(match[g[x][i]]==-1||dfs(match[g[x][i]])){match[g[x][i]]=x;return true;}}return false;}int hungary(){tot=0;memset(match,-1,sizeof match);for(int i=1;i<=n;i++){memset(vis,0,sizeof vis);if(dfs(i))tot++;}return tot;}

PS:匈牙利算法在其他方面的应用
二分图的 最小顶点覆盖 = 最大匹配 
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”,这种情况也类似可证); 
至此,就说明了匹配边与路径覆盖图中连接两顶点之间边的一一对应关系, 那么也就说明了前面的公式成立!

PS:http://www.cnblogs.com/jackiesteed/articles/2043934.html
DAG的最小路径覆盖是指找最小数目的互相不相交的有向路径,满足DAG的所有顶点都被覆盖.
首先给出公式:DAG的最小路径覆盖数=DAG图中的节点数-相应二分图中的最大匹配数.
那么对应一个DAG,如何构造相应的二分图?对于DAG中的一个顶点p,二分图中有两个顶点p和p',对应DAG中的一条有向边p->q,二分图中有p-q'的一条无向边.二分图中p属于S集合,p'属于T集合.
下面我们来解释上面公式为什么成立,思路参考baihacker神牛:


上图中,对应左边的DAG建立构造右边的二分图,可以找到二分图的一个最大匹配M:1-3',3-4',那么M中的这两条匹配边怎样对应DAG中的路径的边?
使二分图中一条边对应DAG中的一条有向边,1-3'对应DAG图中的有向边1->3,这样DAG中1就会有一个后继顶点(3会是1的唯一后继,因为二分图中一个顶点至多关联一条边!),所以1不会成为DAG中一条路径中的结尾顶点,同样,3-4'对应DAG中3->4,3也不会成为结尾顶点,那么原图中总共4个顶点,减去2个有后继的顶点,就剩下没有后继的顶点,即DAG路径的结尾顶点,而每个结尾顶点正好对应DAG中的一条路径,二分图中寻找最大匹配M,就是找到了对应DAG中的非路径结尾顶点的最大数目,那么DAG中顶点数-|M|就是DAG中结尾顶点的最小数目,即DAG的最小路径覆盖数.

0 0
原创粉丝点击