二分图匹配

  今天上算法课的时候老师讲了图的匹配问题,想起来自己以前做ACM的时候学的二分图匹配,之前没有写博客的习惯,现在太忙,好多东西容易忘,还是记录下来比较好。

  求二分图的最大匹配有两种方法,一种是构造一个“超级源点”,一个“超级汇点”,求最大流。还有一种就是匈牙利算法,其实匈牙利算法的本质也是找增广路,两种方法实际上是等价的。

  那么,增广路是如何定义的呢?首先,增广路有奇数条边,例如A->B->C->D->E->F,那么A->B,C->D,E->F是未匹配边;B->C,D->E为匹配边。也就是说一条增广路上有奇数条未匹配边和偶数条匹配边。为什么叫它增光路呢,大家想想,在这条增广路上,我们将匹配边和非匹配边互换,这样会使的匹配边的数量+1(如下图所示)。好了,那么求最大匹配的问题就变成了找增广路的问题,dfs搜索即可。

知道了算法的主导思想之后,实现起来就非常容易了,找到一条增广路,res++即可。

 1 bool dfs(int v){
 2     used[v] = true;
 3 
 4     for(int i = 0; i < G[v].size(); ++i){
 5         int u = G[v][i], w = match[u];
 6         //如果u是未匹配的节点,或者存在与u节点匹配的点w起始的增广路
 7         //怎我们找到了一条从v起始的增广路
 8         if(w < 0 || !used[w] && dfs(w)){
 9             match[v] = u;
10             match[u] = v;
11             return true;
12         }
13     }
14 
15     return false;
16 }
17 
18 int bipartite_matching(){
19     int res = 0;
20     memset(match, -1, sizeof(match));
21 
22     //遍历所有节点,搜索增广路
23     for(int v = 0; v < V; ++v){
24         if(match[v] < 0){
25             memset(used, 0, sizeof(used));
26             //如果dfs返回true,说明搜索到 一条增广路,res++
27             if(dfs(v)){
28                 res++;
29             }
30         }
31     }
32 
33     return res;
34 }
View Code

====================================================================================

另一个值得关注的问题就是最小覆盖覆盖问题,学竞赛的同学们都知道一个重要的结论,那就是在二分图中,|最小顶点覆盖|=|最大匹配|。

为什么呢?

证明:

首先,比如我们找到了一个最大匹配,对于每一条匹配e的两个匹配点x和y。x和y中最多只有一个点连着费匹配边,否则(如下图所示),b->y->x->a就是一条增广路了,与图中不含增广路相矛盾。现在我们明白,对于每一条边,如果该边只有一端连着匹配点,则选择这个匹配点,如果两边都连着匹配点,则任意选一个点即可。但可能有人会想,会不会出现这种情况?(如下图所示),x连着非匹配点,所以我们必须选x,B连着非匹配边我们必须选择B,那么就没有点覆盖A-y了,我们仔细观察发现,显然不可能,因为图中这种情况就是一条增广路。

好了,我们用这种方法找到了一个覆盖点的可行解,那么怎么保证最小呢?很明显,这样选是最小的,因为我希望用更少的点去覆盖是不可能的,因为覆盖这几条匹配边就至少需要这么多点才能完成。

posted @ 2016-11-15 20:33  zhazhalovecoding  阅读(258)  评论(0编辑  收藏  举报