二分图

135 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第28天,点击查看活动详情

什么是二分图

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

听上去比较抽象,那么用图来表示吧

image.png

如图:如果一堆顶点可以被区分为UV两部分集合,且所有边两个顶点都恰好一个属于U集合,一个属于V集合,每个集合内部的点都没有相连,那么这个图就是二分图。

那么对应一边三个订单的二分图我们有好几种方案连线

image.png

在图示方案里面,我们可以发现,右边的方案更好,我们把一种方案叫做一种匹配,那么如何求二部图的最大匹配(匹配数最多)?最容易想到的就是画出全部的匹配,然后输出最大匹配,但是如果顶点非常多呢?显然这种方式不可取。

怎么求二部图的最大匹配:

假设我们有ABC三类人,A分A1、A2,B分B1、B2,C分C1、C2,且A1,A2可以匹配,A1,B2也可以匹配,B1和B2,B1和C2可以匹配,C1和A2可以匹配。那么如何求最大匹配?

我们可以这样想:如果从A1开始考虑,那么A1可以和A2匹配,接下来考虑B1,B1和B2匹配,接下来C1,可是我们却发现C1只能和A2匹配,可A2已经和A1匹配了,那这样肯定不是最大匹配了。所以让C1去找A2,这时候A1空下了,那么A1去找他的另外一个B2,这样B1空了,去找C2.最后就凑成了三对了。

那么如何用代码实现呢?

算法思想:

  1. 假设一个没有匹配的点u开始,从u的边中任意选择一条边(假设匹配的u->v)开始配对。如果点v还没有配对,就配对成功,如果有配对,就需要进行‘询问’,如果询问成立了,就找到了新的关系,可以跟新原来配对,我们可以用一个数组来记录配对关系,如果u->v成功那么数组[v]=u,配对成功后,要把配对数+1,配对过程我们可以使用深度优先算法。
  2. 如果配对失败,从点u开始另外选一个边,直到u配对成功,或者尝试了u所有边为止
  3. 对接下来的点耶进行上面过程。
  4. 输出配对数 那么dfs如下:
int e[100][100];//uv顶点集
int match[100];
int book[100]; //标记已经访问的点
int n,m;

int dfs(int u){
int i;
for(int i=1;i<=n;i++){
if(book[i]==0&&e[u][i]==1){
book[i]=1;//标记已经被访问过
//如果没有配对
if(match[i]==0||dfs(match[i]){
match[i]=u;
return 1;
}
}
}
return 0;
}

按照图,我们可以想到,如果一个二分图有n个节点,那么最多n/2条匹配,如果图中有m条边,那么没找一条路径最多把所有边遍历一遍,花费时间是m,总的时间复杂度是O(NM)。