二分图的定义详见百度百科,简单的理解就是:二分图中每个集合中任两点没有直接相连的边;换言之,边连结的两点必然分别是两个集合的点。
上图是一个二分图,其中{1,2,3,4}为一个集合,我们记为A;{5,6,7,8}是一个集合,我们记为B。我们可以看到,如果把B撇开,仅看A,是没有边的;对于B也同理。每条边都是分别连接A集合中一个点与B集合中一个点。
匹配:指的是两点之间的一种关系,任何点的匹配数都不会超过1;换言之,已经进行过匹配的点,如不调换匹配关系,无法进行新的匹配。
上图中边表示连结的两点可以匹配。1可以和6、7匹配;2可以和5匹配;3可以和6匹配;4可以和8匹配。但是,1不可以同时和6、7匹配;6不可以同时和1、3匹配。假如现在1、6已存在匹配关系,那么调整匹配关系的时候,必须先移除1与6的匹配关系,再让1与7进行匹配。
求最大匹配,即是求这个图最多的匹配数目。求解这一问题的方法主要是匈牙利算法。
从任意一个集合出发,对该集合每个结点都尝试做一遍匹配;遇到冲突的情况,则通过递归调整其冲突结点的匹配关系。最后若匹配成功则匹配数++。
以上图为例,从A集合出发,用link[i]表示与i结点匹配的结点编号。
对于1结点,枚举所有与它相连的边,发现6未匹配,即link[6]==0,于是1与6匹配,即link[6]=1,匹配成功,返回true,ans++;
对于2结点,2与5匹配,link[5]=2,匹配成功,返回true,ans++;
对于3结点,找到6,发现link[6]!=0,递归找到1,重新对1进行匹配;
从1结点找到7,发现link[7]==0,1与7匹配,link[7]=1,link[6]调整为0,匹配调整成功,返回值true;
所以3与6匹配,link[6]=3,匹配成功,返回true,ans++;
对于4结点,找到8,发现link[8]==0,4与8匹配,link[8]=4,匹配成功,返回true,ans++。
所以ans==4,上图最大匹配数为4.
代码:
bool find(int now)
{
for (int i=1;i<=m;i++)
if (!cover[y[i]]&&map[now][y[i]])
{
cover[y[i]]=true;
if (link[y[i]]==-1||find(link[y[i]]))
{
link[y[i]]=now;
return true;
}
}
return false;
}
int main() //x表示A集合,y表示B集合
{
for (int i=1;i<=n;i++)
{
memset(cover,false,sizeof(cover)); //cover用于防止一个点在调整匹配时被同一个点重复匹配
if (find(x[i])) ans++;
}
}