最近开始学习图论的二分匹配,关于最大匹配做一次小总结,希望自己后面回头来看一目明了,也对刚接触的人有帮助:
ps:开始有的文字很多....对于很多人来说一看到文字就烦啦...不过这个总结是针对匈牙利算法的总结,后面的匈牙利代码中有详细说明并结合图片说明,相信对刚接触的人会有帮助
个人觉得对于一个知识点最好是先知道这个”东东“是什么,然后在学习概念比较好,关于先知道是个什么“东东”--就是把这个知识点简单明了化,然后去了解其中令人头疼的概念!
关于二分匹配的最大匹配:
如果有G1、G2、G3三个女孩,B1。B2。B3三个男孩,有一天老师说要调整座位,就了解三个女孩的想法(想和哪一个男孩坐在一起----男孩的想法不考虑),G1说和三个男孩任何一个坐一起都喜欢,G2就说只想和B1坐一起,G3只想和 B2坐一起,那么老师改怎么安排座位,让尽可能多的女孩满意?这里就引入匹配:结合图形理解:
可以看到让G1和B3坐一起(配对/匹配)G2和B1一起,G3和B2一起,那么三个人都会满意,如果换成其他的方案,就不会是三个女孩都满意啦...那么这就是最大匹配
--------------------------------------------------------------------------------------------------------------------------------------------------------在大概了解了最大匹配是什么东东之后,就有必要了解一些概念啦,推荐:基本概念术语
下面是关于求最大匹配的匈牙利算法:
令G = (X,*,Y)是一个二分图,其中,X = {x1,x2,...xm}, Y = {y1,y2,...yn}。令M为G中的任一个匹配。
1)讲X的所有不与M的边关联的顶点标上(@),并称所有的顶点为未被扫描的。转到 2)。
2)如果在上一步没有新的标记加到X的顶点上,则停止。否则转到 3)。
3)当存在X被标记但未被扫描的顶点时,选择一个被标记但未被扫描的X的顶点,比如,xi,用(xi)标记Y的所有顶点,这些顶点被不属于M且尚未标记的边连到xi .现在,顶点xi是被扫描的。如果不存在被标记但未被扫描的顶点,则转到 4)。
4)如果在步骤 3)没有新的标记被标到Y的顶点上,则停止。否则,转到 5)。
5)当存在Y被标记但未被扫描的顶点时,选择Y的一个被标记但未被扫描的顶点,比如yi,用(yi)标记X的顶点,这些顶点被属于M且尚未标记的边连到yi.现在,顶点yi是被扫描的。如果不存在被标记但未被扫描的顶点,则转到 2)。
也可以叙述为:
[ZZ]匈牙利算法
关键在于匈牙利算法的递归过程中有很多重复计算的节点,而且这种重复无法避免,他不能向动态规划一样找到一个“序”将递归改为递推
下面结合hdu2063模板详细解释匈牙利算法:
题目和上面的座位匹配差不多
// 1216k 15ms #include<stdio.h> #include<string.h> #define MAX 501 int map[MAX][MAX];//map[i][j]=1表示i想和j一起,为0就是没有想一起的想法 int link[MAX];//link[i]=t就表示和i配对/匹配的是t!这里注意i表示是i号男生和t号女生配对 int useif[MAX];//useif[i]只有1和0两个值,表示i是不是当前考虑要匹配的,为1就是当前考虑i要匹配 //看了几个定义之后就从main函数中开始看
int n,m,k;
bool dfs(int t)
{
for(int i=1;i<=n;i++)//这里对于女孩t,考虑每一个男生能否和t匹配
{
if(!useif[i] && map[t][i])//i号男生没有被考虑,并且t想和i一起
{
useif[i]=1;//标记考虑
if(link[i] == -1 || dfs(link[i]))//如果i没有匹配的女生(link[i]=-1)或者--这个在开始有点难理解,后面给出一段话结合图片有助理解,这里只要明白这语句的意思是:if(t能和i匹配)
{
link[i]=t; return true;//能匹配就把link[i]更新为t,返回true
}
}
}
return false;
}
int match()
{
int sum=0;
memset(link,-1,sizeof(link));//匹配之前,每个人(题目中是男孩)匹配为-1
for(int i=1;i<=m;i++)//这里,对于每一个女孩i,去选一个男孩匹配
{
memset(useif,0,sizeof(useif));//初始化对于女孩i,没有考虑任何男生
if(dfs(i))//看i能不能从男生中选一个匹配,调用dfs
sum++;//能匹配就产生一对啦....就加一
}
return sum;//返回能产生的对数
}
int main()
{
while(scanf("%d",&k),k)
{
scanf("%d%d",&m,&n);
int i,j;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
map[i][j]=0;//初始化都没有想法
int a,b;
for(i=0;i<k;i++)
{
scanf("%d%d",&a,&b);//对于每个a想和b一起就赋值map[a][b]=1
map[a][b]=1;
}
printf("%d
",match());//开始match匹配,调用match
}
return 0;
}
这里对于上面的if加以说明:
拿案例来说(左边的是女生的标号,右边是等待女生选择的男生标号)
1 1
1 2
1 3
2 1
2 3
3 1
最开始我们用女孩一号去匹配得到如下图:
然后我们对于G2去选择,此时我们依旧从1号男生开始考虑,因为useif[1]在女孩2开始匹配的时候初始化为0啦,并且女孩2也想和男孩1匹配,就标记useif[1]为正在考虑,然后发现link[1]=1(也就是1号男生被1号女生选走啦)但是,确定是否2号女孩能和1号男孩匹配的还有一个条件,就是dfs(link[1]),意思是看能不能把和1号男生匹配的1号女生再去选择另一个男生配对,那么这个函数调用,我们的女1号就选择的2号男生,所以,这里的2号女生就可以和1号男生在一起啦...(有图有真相):
-----那么,后面就是一样的啦...
二分匹配之最大匹配学习总结完毕.......