• 二分图简介


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



    简而言之,就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集

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

    给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

    选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
    如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配.
     
    二分图的最大匹配有许多实际的应用。例如,把一个机器集合L和要同时执行的任务集合R相匹配,E中有边(u,v),就说明一台特定
    机器u(属于L)能够完成一项特定任务v(属于R),最大匹配可以为尽可能多的机器提供任务

    最大匹配在实际中有广泛的用处,求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。即回溯法,但是这个算法的复杂度为边数的指数级函数。因此,需要寻求一种更加高效的算法。

       二分图的最大匹配有2种实现,网络流和匈牙利算法。(《算法导论》第26章有讲网络流算法)

    求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的.但是这个算法的复杂度为边数的指数级函数.因此,需要寻求一种更加高效的算法.

    匈牙利算法是求解最大匹配的有效算法,该算法用到了增广路的定义(也称增广轨或交错轨):若边集合P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。((举例来说,有A、B集合,增广路由A中一个点通向B中一个点,再由B中这个点通向A中一个点……交替进行)。

    由增广路径的定义可以推出下述三个结论:

        1.  P的路径长度必定为奇数,第一条边和最后一条边都不属于M。

        2.  P经过“取反操作”(即非M中的边变为M中的边,原来M中的边去掉)可以得到一个更大的匹配M’。

    (我画的例子:

     

    路径A-F为路径P,BC和DE属于M,可以看到在路径p上属于M和边和不属于M的边交替出现。先黑后红,再黑,在红。

    由于增广路规定是连同2个未匹配的顶点。所有P的长度一定是奇数。

     P经过“取反操作”(即非M中的边变为M中的边,原来M中的边去掉)可以得到一个更大的匹配M’。可以从图中看出来,

    把原来匹配的边(红色表示)去掉,把黑色的边加上,匹配数由2变成了3.增大了。(大概增广路径的名字也是这么来的吧!)

    3.  M为G的最大匹配当且仅当不存在相对于M的增广路径。

    百度百科增广路:http://baike.baidu.com/view/3585026.htm

    从而可以得到求解最大匹配的匈牙利算法:

    (1)置M为空

    (2)找出一条增广路径P通过“取反操作”获得更大的匹配M’代替M

    (3)重复(2)操作直到找不出增广路径为止

    根据该算法,可以选择深搜或者广搜实现,下面给出易于实现的深度优先搜索(DFS)实现。

    //prototype


    int    n, m, match[100];                        //二分图的两个集合分别含有n和m个元素,match[i]存储集合m中的节点i在集合n中的匹配节点,初值为-1。
    bool    visited[100], map[100][100];                 //map存储邻接矩阵。


    bool DFS(cosnt int &k)
    {
          for(int i = 0; i < m; i++)
               if( map[k][i] && !visited[i]    )

              {

                   visited[i] = true;
                 if( match[i] == -1 || DFS(match[i]) )   //寻找是否为增广路径

             {

               match[i] = k;            //路径取反操作。

               return true;

                  }
              }
           return false;
    }

    int main(void)
    {
    //...........

           int     count = 0;

           memset(match, -1, sizeof(match));
           for(i = 0; i < n; i++)

          {    //以二分集中的较小集为n进行匹配较优
                 memset(visited, 0,sizeof(visited));
                if( DFS(i) )     ++count;    //count为匹配数
           }
    //............
    return 0;
    }

    优点:实现简洁,容易理解,适用于边比较多的图,DFS找增广路快.

    相比较而言,广度优先搜索的优点:适用于稀疏二分图,边较少,增广路较短。(BFS实现略)

             另外,二分图最大匹配可以转换成最大流问题来解。假设二分图的两个顶点集分别为X, Y,那么我们在图中添加一个源s,和一个汇t。同时,在s与X的每个顶点间加一条有向边(s, vx),在Y的每个顶点与t间加一条有向边(vy, t)。X与Y的每条边也变为X->Y的有向边。图中每条边的容量为1。则最大匹配问题就转换为求转换后的图G'的最大流的问题。利用Ford-Fulkerson方法求最大流,时间复杂度为O(VE), V为顶点数,E为边数。具体证明参考《算法导论》。但是,经典的求二分图最大匹配的算法是Edmond于1965年提出的匈牙利算法。


    题目:

    JOJ 1102    Courses http://acm.jlu.edu.cn/joj/showproblem.php?pid=1102   (最基本的二分图匹配)

    JOJ   2393   The problem of a CEO  http://acm.jlu.edu.cn/joj/showproblem.php?pid=2393(二分图匹配+字符串处理)


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Fandywang_jlu/archive/2008/03/20/2201351.aspx

    熟悉的陌生人分析

    原题

    大家都知道facebook用户都是双向的好友,a是b的好友,那么b一定是a的好友,现在给定一个用户列表,其中有些用户是好友,有些不是,请判断,这些用户是否可以划分为两组,并且每组内的用户,互相都不是好友。如果能,请给出这个划分。

    例子1: 用户:{1, 2, 3} 好友关系:1-2, 2-3 划分:{1,3} {2}

    例子2: 用户{1,2,3,4} 好友关系:1-2, 2-3, 3-4,4-1 划分:{1, 3}{2, 4}

    分析

    有很多面试题比较直接,看到题目,面试者很自然的就想到用什么方法,这时候,往往面试官要的是bug-free的code;还有一些问题,不那么直接,那就是考察,应试者的分析问题、分解问题的能力。往往是一个相对复杂的问题,分解为我们见过的、相对简单的问题;还有一类问题,从实际当中来,这就需要有建模的能力,也可以说是把问题抽象出来的能力,然后才是分解成小的问题等。这样的题目,能够全面的考察应试者的能力。例如今天的面试题。

    今天的面试题来自facebook的实际问题。像在我们国内,有很多同学在做新浪微博的数据挖掘,好友推荐、关系预测、圈子发现、转发分析、影响力分析等等,这些都是很实际、很有用的问题,每一个问题,都可以作为一个值得深入探讨的面试题。

    相对而言,今天的面试题目比较简单。因为建模的部分,已经在题目中给出了。稍想一下,图分割的问题。facebook的好友关系是双向的,意味着是无向图(新浪微博,twitter都是有向图)。然后,要把图划分为两组,这两组满足什么条件呢?组内没有边,组间有边。这显然就是一个二分图。

    那问题很明显了,就是facebook好友关系构成的图,是否是一个二分图?如果是,请找到这个划分。就是二分图判断+找到一个划分即可。

    根据二分图的特性,一条边上的两个点,肯定是属于不同的组。如果它们出现在同一个组中,肯定就不是二分图了。怎么判断,一条边上的两个点,分属于不同的组呢?我们需要遍历图,如果找到一条边,两个节点,都在同一组,则不是二分图;如果图遍历完成之后,没有找到这样的边,则是二分图。我们在遍历的过程中,我们需要区分,一条边的两个节点分属于不同的组,这里我们用到了染色法。核心思想如下:

    从某一个点开始,将这个节点染色为白色,并且开始广度优先遍历,找到与其相邻的节点,如果是二分图,相邻节点的颜色都应该不同。如果是黑色,则不变;如果是无色,则染成黑色;如果是白色,也就是同色,程序退出。当图遍历完毕时,没有相邻节点同色的,则是二分图,标记为白色和黑色的两组就是一个划分。

    来看两个例子,第一个图中的例子2:

    步骤 遍历节点 相邻节点 队列
    1 1:白 2:黑,4:黑 2,4
    2 2:黑 1:白,3:白 4,3
    3 4:黑 1:白,3:白 3
    4 3:白 2:黑,4:黑

    队列空,结束遍历。找到划分{1,3}白{2,4}黑。

    再看一个不是二分图的例子: 用户{1,2,3,4} 关系:1-2,1-3,1-4,2-3,3-4.

    判断步骤如下:

    步骤 遍历节点 相邻节点 队列
    1 1:白 2:黑,3:黑,4:黑 2,3,4
    2 2:黑 1:白,3:白 3,4
     

    发现3矛盾了,则上面的关系,不够成二分图。

    【分析完毕】

  • 相关阅读:
    python mysqldb 模块学习
    接口自动化学习笔记
    unittest 单元测试框架断言方法
    Python+Django+Bootstrap 框架环境搭建
    Django CSRF cookie not set.错误
    django安装与卸载
    jmeter+ant+jenkins接口自动环境搭建
    jmeter 非GUI模式下测试报错An error occurred: Unknown arg:
    非 GUI 模式运行 JMeter 压力测试
    Android SDK开发包国内下载地址
  • 原文地址:https://www.cnblogs.com/youxin/p/3352087.html
Copyright © 2020-2023  润新知