最大匹配:
首先写一下我自己对最大匹配的理解,首先举个找对象的例子,这个例子在我计算机科学中的数学基础的老师龙环的课上讲过,就是男女分成两部分,一个男的喜欢多个女的,一个女的也可以喜欢多个男的,这时候我们要找出能够匹配出最大对数的方法,这个就是最大匹配。
求最大匹配的算法就是匈牙利算法很简单的递归算法,就是对于每个男生,扫描他所中意的女生,若是那个女生没有匹配对象的话,我们就直接给他们设定一个匹配的关系,如果这个女生有匹配对象的话,我们就得尝试着去找一下有没有增广路径,增广路径的大概意思就是 B男喜欢A女和B女 C男喜欢B女,B女已经和B男匹配了,这个时候如果拆掉B男和B女的匹配 让C男匹配B女,B男匹配A女就成功增加了一个匹配,当然这个过程是可以递归下去的,意思就是若是A女已经有了匹配的对象,就尝试着拆散和A女匹配的对象,看下能不能给这个对象找到相匹配的另一个人,当然为了使递归停止,我们必须设定一个VISIT数组,每次开始寻找之前,把这个数组全部置0,如果访问过了一个女生就要把相应的值改成1。
按照如此的方法,我们枚举遍所有的男生,最后求出的就是最大匹配。
下面讲一下什么是最小覆盖,这个不止是二分图的概念,而是所有图中的概念,大概意思就是我能求出的最小的点集,使得边集中的所有边都至少有一个点在这个点集中
最后证明一下在二分图中为什么最大匹配等于最小覆盖这个我参考了一下http://www.matrix67.com/blog/archives/116这个兄弟的博客,他里面讲的我觉得挺好的,可是评论下面还是有人说看不懂,我就讲一下我的理解
首先要求出最大匹配,发现个数为M,然后自己构造出了一个大小为M的点集,最后证明这个点集就是最小覆盖。
构造方法就是从右边所有未被匹配的点出发去走增广路径,当然是不可能走出增广路径的,只能走出不完整的增广路,标记这些路径上所有经过的点,最后选择左边的被标记的点,和右边未被标记的点,作为最终求出的点集。
首先证明为什么这个点集的大小是M,首先右边的未被标记的点一定是匹配中的点,因为若不是匹配中的点一定会被标记。其次左边被标记中的点也一定是匹配中的点,因为假如左边不是匹配中的点的话肯定不会走到那个点上去,因为从右边走到左边需要满足走的那条边是匹配边,最后证明不可能有一个匹配边左边被标记,而右边未被标记,按照我们的规则,左边的点要根据他的匹配边再走到右边以寻求增广路径。所以经过以上说明我们就知道所有的匹配边都可以从得到的点集中寻找出有且只有一个点是它两点中的一个。
其次我们证明这个点集能覆盖所有的边,这个我认为参考博客中写的太麻烦了,假设有一条边不被点集覆盖,说明这个边没有点在匹配中,说明这个边没有被匹配,所以就可以找到另一个匹配,这样跟我们找到最大匹配的假设是矛盾的。最后我们找到了最小点覆盖,并且这个覆盖肯定是最小的,因为我们有M个匹配,不可能找出比这个M更小的点集去覆盖所有的边。
最后贴一下另一个博客中的求最大匹配的代码:
参考https://www.cnblogs.com/shenben/p/5573788.html
#define maxn 10//表示x集合和y集合中顶点的最大个数!
int nx,ny;//x集合和y集合中顶点的个数
int edge[maxn][maxn];//edge[i][j]为1表示ij可以匹配
int cx[maxn],cy[maxn];//用来记录x集合中匹配的y元素是哪个!
int visited[maxn];//用来记录该顶点是否被访问过!
int path(int u)
{
int v;
for(v=0;v<ny;v++)
{
if(edge[u][v]&&!visited[v])
{
visited[v]=1;
if(cy[v]==-1||path(cy[v]))//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
{
cx[u]=v;
cy[v]=u;
return 1;
}
}
}
return 0;
}
int maxmatch()
{
int res=0;
memset(cx,0xff,sizeof(cx));//初始值为-1表示两个集合中都没有匹配的元素!
memset(cy,0xff,sizeof(cy));
for(int i=0;i<=nx;i++)
{
if(cx[i]==-1)
{
memset(visited,0,sizeof(visitited));
res+=path(i);
}
}
return res;
}