下面我还是举个啊哈算法书上的过火车的例子进行讲述;
基本情景是有3个男生3个女生,1号女生认识1号男生和2号男生,2号女生认识2号男生和3号男生,3号女生认识1号男生;
现在来讲他们进行分配,俩俩坐一起去坐火车(认识的人才能坐一横排并且一横排就有两个位置),问现在能够分配多少对男生女生一起坐火车;
当然了既然问了就要找出最合理的方案让尽可能多的人一起去坐火车,本着这样的原则我们就开始进行分配了:
下面我先给出节点信息
5(相互认识的男女数量) 3男生3 女生
1 1(1号女生和一号男生相互认识)
1 2
2 2
2 3
3 1
从头开始找:1号女生认识1号男生所以算是配对成功,下面轮到2号女生也是认识2号男生也成功了,现在该3号了但是3号女生只认识1号男生但是1号男生已经分出去了,现在该怎么办?只能尝试着拆开已经配好的看看1号男生能不能空出来配给1号女生,下面是当前状况的图文版
此时先把1号女生和1号男生先拆开:让1号女生找下一个他认识的男生找到2号但是2号男生配给了2号女生那就先拆开2号男生和2号女生,2号女生去找其他认识的男生找到了3号男生刚好可以,所以2号女生就和3号男生匹配1号女生就和2号男生匹配这样1号男生就空出来了,所以这个时候3号女生改笑了他找到了一起做车的人所以现在最大限度的分成了三组
下面的话我就来说一下过程:
第一步:从任意一个点u开始配对,从它的任意一条边中选出一条假设为(u->v),然后开始配对,,要是此时v还没有配对就配对成功,此时就是找到了一条增广路。如果此时尝试连锁反应(也就是说上面用一大堆话讲述的3号女生如何在1号男生已经配对的情况下有进行配对成功的),如果配对成功就定义一个数组natch去配对成功的节点之间的信息match[v]=u;然后配对成功的数目加一,上面的配对可以通过用深搜来解决
第二步:
如果刚尝试u的第一条边v已经配对而且也尝试失败的情况下就尝试u的另一个出边,直到u配对成功或者尝试完所有边;
第三步:接下来去找所有的点开始进行上述操作(和u在同一集合里面)
上面的步骤基本就是二分匹配的思想:
下面的话我就给出一个模板题目的答案吧,这道题目也可以说是完全套用模板来解出来的
这个模板应该还是比较好理解的;二分除了这一类算法有
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2:最大独立集 = 顶点数 - 最小点覆盖数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
今天算是了解了最大匹配数其他的有待更新下一篇博客
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=2063
AC代码:
#include<stdio.h>
#include<string.h>
int e[1010][1010];
int book[1010],match[1010];
int n,m,k;
int dfs(int x)
{
for(int i=1; i<=m; i++)
{
if(book[i]==0&&e[x][i]==1)
{
book[i]=1;
if(match[i]==0||dfs(match[i]))
//match[i]也就是递归回去重新分配(
//让已经分配好的找下一个看能不能成功也可
//以说是空出位置来让当前的u和当前的i进行配对)
//建议在这里自己进行模拟一遍走一遍过程看看就会好理解一些
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
int sum,t1,t2;
while(scanf("%d", &k),k!=0)
{
memset(match,0,sizeof(match));
memset(e,0,sizeof(e));
sum=0;
scanf("%d %d", &n,&m);
for(int i=1; i<=k; i++)
{
scanf("%d %d", &t1,&t2);
e[t1][t2]=1;
}
for(int i=1; i<=n; i++)
{
memset(book,0,sizeof(book));
if(dfs(i))
sum++;
}
printf("%d\n", sum);
}
return 0;
}