一、定义

即在一个二分图中,找到最大的匹配对数。每个点只能最多和另外一个点配对。

二、情景

为了方便理解,这里考虑一下最大匹配场景:

  • 有一列男生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; 
}

全原创