美文网首页
算法导论——二分图最大匹配

算法导论——二分图最大匹配

作者: Myth52125 | 来源:发表于2017-09-19 21:12 被阅读0次

    二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

    这是个逗比博主写的匈牙利算法和KM算法

    通俗点讲:就是将图中的节点分到两个集合中,满足:只存在由一个集合中的点指向另一个集合中的点的边。(也就是说两个集合中点不互通)

    二分图的一个等价定义是:不含有「含奇数条边的环」的图

    因此,如果存在环的话,环的边数只能是偶数,这样才能均分到两个集合中。如果是奇数一定不是二分图。

    匹配
    边指向一个点成为匹配。已匹配的点不能够再次匹配。
    如图,基数为2的匹配

    匹配
    最大匹配
    最大基数的匹配。
    上面图中最大匹配为: 最大匹配

    完美匹配
    两个集合中的点,两两匹配。点都是匹配点,但是边不一定是匹配边

    判断是否是二分图

    使用深度优先搜索,从任意一点开始遍历,对一点进行染色,其相邻如果为染色则染为不同的颜色,如果存在已染色且染色和相邻节点相同,那么表示存在环路(因为环路所以才被染色),且环路边数为奇数(因为相同颜色),那么不是二分图。

    匈牙利算法

    寻找二分图中的最大匹配

    假设:

    1. 二分图中所有的节点都在一个环上,或者一条通路上。
      那么我们只要找到这条最长的路径即可算出最大匹配。
      比如最长路径为4,那么最大匹配为2
    2. 假设二分图是完美匹配
      这样我们需要遍历每一个点,然后对已经匹配过的点进行标记。就自然的获得总点数/2的最大匹配

    但实际中二分图中并不是单纯的一个环或者是完美匹配。二是两者结合,比如存在几对点的完美匹配,和几个环,或者几个通路。而且通路和环又相互叠加。

    解决方法也是上面两种假设的解决方法的叠加:寻找最长通路(路径)和遍历

    在二分图中找到的路径总是在两个集合中跳:类似于,左->右->左->右....
    因为匹配的概念是左右两个集合中的点一一对应。所以这条路径中的边一定也是匹配边和非匹配边相互交叉。
    比图,第二个右,如果左->右是一条匹配边,那么右->左就是非匹配边。
    这种路径叫做增广路径

    在匈牙利算法中,我们一边寻找最长增广路径,一边去遍历那些可能的一一匹配的点。

    struct Edge
    {
        int from;
        int to;
    };
    
    
    
    int hungarian_dfs()
    {
        int counts=0;
       //用来记录二分图中的节点数目
        int maxNode=10;
        //用来记录节点是否已经匹配过了
        vector<int> matched(maxNode,-1)
        //用来记录在一个递归增长增广路径时,某个节点是否被添加了
        //因为找到了一条比原来长的增广路径以后 直接return,所以没有必要回溯
        vector<bool> memo(maxNode,false);
        //边的集合,记录每个节点指向的边
        vector<vector<Edge>> edges; 
    
        for(int j = 0 ;j<V.size();j++)
        {
            if(metched[j] == -1 )
            {
                memo=vector(maxNode,false);
                if(dfs_(j))
                {   
                    counts++;
                }
    
            }
        }
        return counts;
    }
    
    
    bool dfs_(int u)
    {
        //遍历该节点指向的边,去寻找一条更长的增广路径
        for(int i=0;i<edges[u].size();i++)
        {
            //在寻找增广路径时,一个节点没有被添加在该增广路径中是继续
            if(memo[edges[u][i].to] == false)
            {
                //添加到路径中
                memo[edges[u][i].to] == true;
                
                //matched(edges[u][i].to) == -1 表示找到了一条更长的路径。
                //或者是找到了一条新的路径。可能是全新,也可能是分叉
                //dfs_(matched(edges[u][i].to)) 如果该节点已经在一条增广路径中了,
                //那么递归的去寻找一条更长的
                if(matched(edges[u][i].to) == -1 
                    || dfs_(matched(edges[u][i].to)))
                {
                    //这两句,纯粹的表示这两个点已经在一条增广路径中了
                    matched(v)=u;
                    matched(u)=v;
                    return true;
                }
            }
        }
        return false;
    }
    

    dfs_中,通过遍历点,不断的去增长增广路径,每次增加1(只是说在之前找到的基础上增加了,但是该路径的开头却没有了。),或者是寻找新的分叉和新的路径(通常是一对点,一一匹配)。
    每次如果增广路径增加的时候,dfs_的参数,和最终找到的matched(edges[u][i].to) == -1,被增加到路径中,所以每次都增加两个节点。
    这样可以确保counts++
    其中保存的metched中存放的就是匹配好的对。

    类似流程为:

    1. 第一轮:A->B
    2. 第二轮:C->B
      递归dfs_(B)给B匹配好的A从新寻找一个新的对象,让C能够匹配B,3而A去匹配新找到的。
      C->B->A->D
      如果递归没找到,那么C重新从新再去匹配一个,不在匹配B
      C->E
    3. 第三轮F->B
      同第二轮。
      如果F只指向B,那么F将不能匹配到对象

    关键字在一个“腾”,让已匹配的再去匹配别的,腾出一个给我。

    KM算法

    带权重的完备匹配。寻找最大权重的匹配
    完备匹配下的最大权匹配

    这种算法只有描述,没有原理,算法

    相关文章

      网友评论

          本文标题:算法导论——二分图最大匹配

          本文链接:https://www.haomeiwen.com/subject/ckxgsxtx.html