匈牙利算法及其模板 - [二分图最大匹配]

引用来自https://www.renfei.org/blog/bipartite-matching.html

二分图:把一个图G的所有顶点划分为两个不相交集 L 和 R ,使得图G中每一条边都分别连接 L , R 中的顶点。如果存在这样的划分,则此图为一个二分图。

匹配:一个「匹配」(matching)是一个边的集合,其中的任意两条边都没有公共顶点。

最大匹配:在一个图的所有匹配中,存在一个匹配,它所含的边的数量是所有匹配中最多的,该匹配称为这个图的最大匹配。

交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边……形成的一条路径叫做交替路。

增广路:对于一条交替路,其终点若为一个未匹配点(出发点不算),则这条交替路称为增广路(agumenting path)。

  (因为交替路的定义使得其,除了路径的起点和终点外,不存在未匹配点)

增广路有一个重要特点:

  非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配,对增广路的增广:只要把增广路中的匹配边和非匹配边的身份交换即可。

  由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。

那么根据增广路定理,找不到增广路时达到最大匹配;

故因此得到匈牙利算法:

从L集的第1个顶点开始,枚举未匹配点 i 进行搜索,寻找增广路。

  若走到了某个未匹配点,则代表寻找到了一条增广路,立即进行增广,匹配边数 +1;

  然后显然 i 点成为一个匹配点,根据交替路的定义所以不再有从 i 点出发的交替路,也就没有增广路,故停止以 i 点为起点的搜索,进入下一个点。

  如果一直没有找到增广路,则也不再从 i 点搜索,直接进入下一个点。

 

匈牙利算法模板:

struct Edge{
    int u,v;
};
vector<Edge> E;
vector<int> G[MAX];
int lN,rN;//二分图L,R集各自的顶点数
int match[MAX];
int vis[MAX];
bool dfs(int u)
{
    for(int i=0,_size=G[u].size();i<_size;i++)
    {
        int v=E[G[u][i]].to;
        if (!vis[v])
        {
            vis[v]=1;
            if(!matching[v] || dfs(matching[v]))//若遇到未匹配点,直接进行增广;否则继续搜索.
            {
                matching[v]=u;
                matching[u]=v;
                return true;
            }
        }
    }
    return false;
}
int hungarian()
{
    int ret=0;
    memset(matching,0,sizeof(matching));
    for(int i=1;i<=lN;i++)
    {
        if(!matching[i])
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i)) ret++;
        }
    }
    return ret;
} 

 复杂度O(|V|*|E|).

posted @ 2017-10-10 20:18  Dilthey  阅读(1198)  评论(0编辑  收藏  举报