今天上算法课的时候老师讲了图的匹配问题,想起来自己以前做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 }
====================================================================================
另一个值得关注的问题就是最小覆盖覆盖问题,学竞赛的同学们都知道一个重要的结论,那就是在二分图中,|最小顶点覆盖|=|最大匹配|。
为什么呢?
证明:
首先,比如我们找到了一个最大匹配,对于每一条匹配e的两个匹配点x和y。x和y中最多只有一个点连着费匹配边,否则(如下图所示),b->y->x->a就是一条增广路了,与图中不含增广路相矛盾。现在我们明白,对于每一条边,如果该边只有一端连着匹配点,则选择这个匹配点,如果两边都连着匹配点,则任意选一个点即可。但可能有人会想,会不会出现这种情况?(如下图所示),x连着非匹配点,所以我们必须选x,B连着非匹配边我们必须选择B,那么就没有点覆盖A-y了,我们仔细观察发现,显然不可能,因为图中这种情况就是一条增广路。
好了,我们用这种方法找到了一个覆盖点的可行解,那么怎么保证最小呢?很明显,这样选是最小的,因为我希望用更少的点去覆盖是不可能的,因为覆盖这几条匹配边就至少需要这么多点才能完成。