关于二分图

 

这里是把自己会的和已经理解的关于二分图的东西小结一下。内容包括:
二分图的定义概述;
什么样的无向图是二分图 ( 二分图一条重要性质的证明 )
二分图匹配 ( 增广路算法、 M 交错路径算法 )
二分图模型的理解, JOJ 1102 Courses JOJ 1122 The Dog Task 两个题目的解法记录。
 
一堆数学公式容易让人望而生畏,所以尽量不使用数学公式来表达,这样诚然会失掉数学的简洁和严密,但是入手也更快。
二分图的定义非常简单,有两组顶点,一组顶点记为 L ,另一组记为 R L R 没有公共的元素,并且所有的边都是连接 L R 中的点的,对于 L R 本身,它们内部的任何两个点都没有边相连,这样的无向图就叫二分图。《组合数学》上这样讲解:二分图可描述为:一,顶点的集合;二,将该顶点集分成两部分的一个划分;三,连接一部分的一个顶点与另一部分的一个顶点的边的集合。
 
二分图是无向图,那么什么样的无向图是二分图呢?有以下定理:
定理    无向图G 为二分图的充分必要条件是,G 至少有两个顶点, 且其所有回路的长度均为偶数。
证明:(在参考资料中给出的第三篇文章中有这个定理的数学证明过程,这里记录的是自己的证明方法)至少两个顶点,这个显然,把这个条件忽略掉,着重考虑回路长度为偶数这一条件。先证充分性:
由于图中可能有回路也可能无回路,无回路的情况应该最简单,自然考虑分类讨论。于是,分类讨论后,充分性的证明转化成以下两个命题:
a)      所有无回路的无向图都是二分图;
b)      所有有回路且回路长度为偶数的无向图都是二分图。
对于 a) ,因为无回路无向图总是能把它画成一棵树,所以,这个命题等价于:所有的树都是二分图 。到这里,命题 a) 证明显然,因为有一种很简单的从树构造二分图的方法:令树的奇数层的结点为集合 L ,令树的偶数层结点为集合 R ,这样就从树得到了一个二分图。
再看命题 b) ,可以把 b) 转化为 a) 。对于图中的每一个回路,我们都从中拿掉一条边,这样可以消灭所有的回路,由 a) 知消灭掉所有回路之后的图是二分图。把此时得到的二分图画成一棵树,拿掉一条边后的回路此时就是树中的一条路径,并且路径的长度为奇数,这就意味着路径的头结点和尾结点所在层数的编号一个是奇数一个是偶数,用上面的从树构造二分图的方法知,头结点和尾结点分别在集合 L 中和集合 R 中,我们再把拿掉的这条边加上去,只不过是在 L R 中的两个顶点间连接了一条边,图仍然是原来的二分图。至此,充分性得证。
再证必要性。假设二分图中的一条回路是 (v0, v1, v2, …, vm, v0) ,由于是二分图,相邻顶点必不属于同一个集合,用 L 标记属于集合 L 的点,用 R 标记属于集合 R 的点,不妨假设 v0 属于 L ,则上面的回路可以标记为 L, R, L, R, …, L, R ,由此可见,回路必有偶数个顶点,因此必有偶数条边。必要性得证。
 
给定一个二分图 G ,我们说 G 的边集的子集 M G 的一个匹配,如果 M 中没有两条边有公共顶点。在 G 的所有匹配中包含边数最多的匹配叫做二分图 G 的最大匹配。下面介绍的算法就是寻找二分图的一个最大匹配的算法。
 
一:基于增广路的求最大匹配的算法
给二分图增加一个源点 s, 一个汇点 t ,并令二分图中的每条边都具有单位容量 1, 于是二分图的最大匹配问题就转化成了在转化后的图中的最大网络流问题。转化后的图很有特点,比如每条边都具有单位容量,因为这一点,找到一条 (s,t) 增广路后增流必为 1, 且增流后在残流网络中所有边反向,这也就意味着不可能再从 s 出发把这条路径再过一遍了,所以,不需要在实际的图中加上一个 s 一个 t ,只需要从 X 中的每个点出发找一次增广路,找到则将流增加 1, 遍历完 X 中的点后,也就找到了最大流。同样,这个图也不必要是有向的。
二:组合数学教材上的基于寻找 M 交错路径的算法
对于二分图的一个匹配 M ,二分图中的一个 M 交错路径满足以下性质:
a)       路径的第一、三、五……边不属于匹配 M
b)      路径的第二、四、六……边属于匹配 M
c)      M 交错路径的起点和终点都不与匹配 M 的边关联。
第一条边和最后一条边不属于 M ,而中间的边又都是交错的,显然 M 交错路径的长是奇数 2k+1, k>=0( 注意当 k=0 时的 M 交错路径的形状 ) ,且不属于 M 的边比属于 M 的边多 1, 把交错路径取反 ( 把路径中原来属于 M 的边踢出去,把原来不属于 M 的边加到 M 中来 ) ,就得到了多一条边的匹配。
这个算法主要步骤就是找交错路径,首先标记所有有可能作为交错路径起点的 X 中的顶点,然后用它们去标记 Y 中的顶点,然后再从 Y 中标记的顶点标记 X 中的顶点,如此,直到没有新标记的顶点时,或者找到了一条交错路径,或者已经找到了最大匹配。
该算法的详细描述及代码实现见参考资料一。
 
二分图匹配解决了一类资源互斥使用的问题,比如《组合数学》中举的非攻击型车问题,多米诺骨牌覆盖棋盘问题,工作指派问题,又如,按照这个思路,八皇后问题也完全可以用二分图匹配来解决,在各个 OJ 都有相关题目,例如 JOJ 1102, 这是一个标准二分图匹配问题,对于 JOJ 1122 ,从第 N 个点出发后,狗应该比主人早到第 N+1 个点,狗的速度是主人的两倍,用这个条件计算出从第 N 个点到第 N+1 个点这条路径上狗有可能去的那些地方,这些地方就是在二分图中和这条路径相关联的点。然后运行一次二分图匹配算法即可。
JOJ 1102 http://acm.jlu.edu.cn/joj/showproblem.php?pid=1102
JOJ 1122: http://acm.jlu.edu.cn/joj/showproblem.php?pid=1122
JOJ 1122 题目 AC 的代码:
#define _CRT_SECURE_NO_DEPRECATE
 
#include <stdio.h>
#include <string.h>
#include <math.h>
 
typedef struct {
     int x, y;
} Coordinate;
 
char graph[110][110];
Coordinate N[101], M[101];
int n, m;
char visited[110], mate[110];
 
double dist(Coordinate p1, Coordinate p2) {
     return sqrt((double )(p2.x-p1.x)*(p2.x-p1.x) + (double )(p2.y-p1.y)*(p2.y-p1.y));
}
 
int findAugumentPath(int s) {
     int i;
     for (i = 0; i < m; i++) {
         if (graph[s][i] && !visited[i]) {
              visited[i] = 1;
              if (mate[i] == -1 || findAugumentPath(mate[i])) {
                   mate[i] = s;
                   return 1;
              }
        }
    }
     return 0;    
}
 
 
int main() {
     int scenario = 0, p;
     int i, j, x, y;
     double d1, d2, d3;
 
     //freopen("in.txt", "r", stdin);
 
     while (scanf("%d%d" , &n, &m), n+m) {
         printf("Scenario #%d:/n" , ++scenario);
 
         for (i = 0; i < n; i++) scanf("%d%d" , &N[i].x, &N[i].y);
         for (i = 0; i < m; i++) scanf("%d%d" , &M[i].x, &M[i].y);
 
         memset(graph, 0, sizeof (graph));
 
         /* construct the bipartile graph */
         for (i = 0; i < n - 1; i++) {
              d3 = dist(N[i], N[i+1]) * 2;
              for (j = 0; j < m; j++) {
                   d1 = dist(N[i], M[j]);
                   d2 = dist(M[j], N[i+1]);
                   if (d1 + d2 <= d3) graph[i][j] = 1;
              }
         }
        
         /* match */
         p = 0;
         memset(mate, -1, sizeof (mate));
         for (i = 0; i < n - 1; i++) {
              memset(visited, 0, sizeof (visited));
              if (findAugumentPath(i)) p++;
         }
        printf("Max interesting places: %d./n/n" , p);
     }
     return 0;
}
 
 
参考资料:
http://www.yuanma.org/data/2006/0625/article_957.htm
这个网站上讲的算法就是由冯舜玺,罗平,裴伟东译,机械工业出版社出版的那本《组合数学》书上讲的算法。
 
http://old.blog.edu.cn/user3/Hailer/archives/2007/1829623.shtml
这篇文章讲的非常非常好。作者讲到了许多与二分图有关的概念、二分图的性质、二分图匹配的各种算法。
 
http://hi.baidu.com/sysucs/blog/item/9abb79595c93a82b2934f035.html
作者用了连续好几篇文章,图文并茂,非常生动严谨地讲解了二分图的基本性质,匹配算法,以及稳定婚姻问题。这是第一篇文章的链接。
 
http://www.608088.com/show-1221-1.html
这篇文章给出了二分图匹配的三种 C++ 代码实现,以及众多的 ZOJ 上的使用二分图匹配算法解决的题目编号。

你可能感兴趣的:(关于二分图)