[模板] 二分图匹配问题——匈牙利算法

Problem

Luogu P3386 二分图匹配

Solution

二分图不一定为无向图,只需边的两端的点可分即是二分图。这是最小边覆盖的前提条件。
匈牙利算法匹配流程:

  1. 确定二分图的两个点集,选择一个点集作为搜索起点。设为左边点。

  2. 两个数组:match, check。match代表匹配点,check代表一次深搜的vis(是否被遍历)。match建议初始化为-1。

  3. 枚举每个左边点。如果左边点没有被匹配过(match为-1),进行深搜匹配。最大匹配不会超过左边点的个数,因此每次深搜最多只增加一对匹配。
    I. 枚举相邻的右点。如果没有被匹配过则直接返回,因为已经增加一对匹配。如果被匹配过了则深搜到右点的匹配点,看看是否有点没被匹配,持续下去。必须找到一个没被匹配的右点,才能形成增广路。

    II. 若找到没被匹配过的点或深搜的点返回了true,更新匹配,返回true。代表这是一个增广路。

    III. 若啥也没找到返回false。代表找不到增广路。

  4. 若深搜左边点返回ture,答案计数+1.代表找到了增广路。

Codes

#include<bits/stdc++.h>
using namespace std;

const int N=2010,M=8e6+10;
int h[N],to[M],nexp[M],p=1;
inline void ins(int a,int b){
    nexp[p]=h[a],h[a]=p,to[p]=b,p++;
}

bool check[N];
int match[N];

bool dfs(int x){
    for(int u=h[x];u;u=nexp[u]){
        if( !check[to[u]] ){
            check[to[u]]=1;
            if( match[to[u]]==-1 || dfs(match[to[u]]) ){
                match[to[u]]=x;
                match[x]=to[u];
                return true;
            }
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false);
    int n,m,e;
    cin>>n>>m>>e;
    int u,v;
    for(int i=0;i<e;i++){
        cin>>u>>v;
        if(v>m) continue;
        v+=n;
        ins(u,v),ins(v,u);
    }
    memset(match,-1,sizeof(match));
    int ans=0;
    for(int i=1;i<=n;i++){
        if(match[i]==-1){
            memset(check,0,sizeof(check));
            if(dfs(i)) ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

扩展阅读

Renfei Song's Blog - 二分图的最大匹配、完美匹配和匈牙利算法

ACDreamer 的匈牙利算法详解

二分图中对最小顶点覆盖、最小边覆盖、最大独立集的理解

posted @ 2017-11-05 19:45 ajcxsu 阅读(...) 评论(...) 编辑 收藏