图的匹配是指任意两条边都没有公共端点,也就是顶点只能一一配对(可能有顶点孤立,但是不能一配多)
如果路径上匹配边和非匹配边交替出现,且首尾是非匹配边,则称此路径为增广路,将增广路上的边的匹配状态取反后,仍然是一组匹配,并且匹配数增加1
最大匹配等价于不存在增广路(若有增广路,匹配数还能加1),可用匈牙利算法寻找增广路来计算二分图的最大匹配
存储结构采用邻接链表,二分图分为A、B两部分,把无向图存成有向图,只存储A指向B的边即可,n1
、n2
为A、B顶点数,m
为A指向B边数
int value[m], next[m], head[n1], d;
memset(head, -1, sizeof(head));
match[j]
标示B中顶点j
所匹配的A中顶点编号,初始化为全-1
表示尚未匹配,vis[j]
标示B中顶点j
是否在当前递归的增广路中(0
为不在,1
为在)
int match[n2], vis[n2];
算法采用深度优先遍历实现,尝试寻找A中顶点x
在B中的匹配,遍历x
所有邻接顶点y
,如果y
尚未匹配或者y
已匹配但可以找到增广路,则匹配成功
(<x, y>是非匹配边,<y, match[y]
>是匹配边,依次类推直到找到尚未匹配的B中顶点,递归形成的就是一条增广路,回溯时将匹配状态取反)
(一个顶点不能在增广路中出现两次,否则递归无法终止,故而需要vis
数组)
bool find(int x) {
for (int i = head[x]; i != -1; i = next[i]) {
int y = value[i];
if (!vis[y]) {
vis[y] = 1;
if (!match[y] || find(match[y])) {
match[y] = x;
return true;
}
}
}
return false;
}
遍历A中所有顶点,统计匹配数量,寻找A中每个顶点的匹配前都要将vis
数组初始化为全0
int cnt = 0;
for (int i = 0; i < n1; i++) {
memset(vis, 0, sizeof(vis));
if (find(i)) cnt++;
}
注解:A和B中顶点编号是独立的,可以有顶点是相同编号