一、定义
即在一个二分图中,找到最大的匹配对数。每个点只能最多和另外一个点配对。
二、情景
为了方便理解,这里考虑一下最大匹配场景:
- 有一列男生1..n,还有一列女生女生n + 1..n + m。即结点分开存;
- 愿意配对的男女会有边相连;
- 要求出男女配对的最大匹配数目。
三、解析
有两种方法可以解决最大匹配问题:匈牙利算法、Hopcroft_karp算法。
匈牙利算法
- 时间复杂度O(VE)
- 优点是代码简单,不超时时优先使用
Hopcroft_karp算法
- 时间复杂度O(sqrt(V)*E)
- 优先时更快,缺点是代码复杂,类似最大流
- 其实该算法就是将匈牙利算法中的find部分通过bfs分段,一点点来找匹配而已。
四、匈牙利算法 模板
bool find(int u){
for(int v : G[u]){
if(vis[v]) continue;
vis[v] = 1;
if(belong[v] == 0 || find(belong[v])){
// 若对象名花无主 或 对象能重新找到npy, 则 可以匹配该对象
belong[v] = u, belong[u] = v;
return 1;
}
}
return 0;
}
int maxMatch(){
int res = 0;
for(int i = 1; i <= n; i ++){ // 男生逐个去邀请追女生
memset(vis, 0, sizeof(vis)); // vis用于标记对象点是否判断过, 每次都要清零
if(find(i)) res ++; // 找到一个心仪的女生了
}
return res;
} 五、Hopcroft_karp算法 模板
/*
类似最大流算法 数组解释:
dist[i]: 层数标记
belong[i]: i 的对象
*/
bool bfs(){
queue<int> queue;
memset(dist, -1, sizeof(dist));
fin_dist = INF; // dist为当前终点的层数标记
for(int u = 1; u <= n; u ++) if(!belong[u]) que.push(u), dist[u] = 0; // 若干起点
while(!que.empty()){
int u = que.front();
que.pop();
if(dist[u] > fin_dist) break; // 到增广路终点层数的点都标记好了就跳出, 后面不再标记
for(int v : G[u]){
if(dist[v] == -1){ // 未被标记
dist[v] = dist[u] + 1;
if(!belong[v]) fin_dist = dist[v]; // 找到增广路, 确定增广路终点层数
else dist[belong[v]] = dist[v] + 1, que.push(belong[v]); // 继续bfs
}
}
}
return dist != INF;
}
bool find(int u){
if(dist[u] > fin_dist) return 0;
for(int v : G[u]){
if(vis[v] || dist[v] != dist[u] + 1) continue;
// 询问过 || 不是上下层关系 则跳过
vis[v] = 1;
if(belong[v] == 0 || find(belong[v])){
// 若对象名花无主 或 对象能重新找到npy, 则 可以匹配该对象
belong[v] = u, belong[u] = v;
return 1;
}
}
return 0;
}
int Hopcroft_karp(){
int res = 0;
while(bfs()){ // 搜索有无增广路
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i ++)
if(!belong[i] && find(i)) res ++;
// 若没有女朋友 && 能找到
}
return res;
}全原创

京公网安备 11010502036488号