二分图的最大匹配

本文介绍二分图最大匹配算法,有关离散数学中的简单知识不再赘述。如果觉得有用不要吝啬给博主点赞哦

一、定义

matching:
给定一个二分图G = (V,E),M为E的子集,如果图中的顶点最多与M中的一条边相连,则称M为二分图的一个匹配。
maximum matching:
当M的基数最大时,就为最大匹配.
free:
如果一个顶点v 不与M中任何一条边相连,即未参与匹配,则状态标记为free
augmenting path:
一条通路P=(v1,v2),……(v2k-1,v2k),
如果起点v1与终点v2k 均为free且P为交错路径则称P为M的增广路径。
shortest augmenting path:
如果增广路径P的基数最小,即拥有最少的边,则为最短增广路径。
vertex-disjoint
如果两个集合中不包含相同的顶点,则称之为顶点不相交的

二、相关定理及证明(离散数学差的同学可跳过此节)

LEMMA 1
如果P是匹配M的一个增广路径,则P⊕M为一个匹配,且|P⊕M|=|M|+1
在这里插入图片描述
THEOREM 1
设M和N为匹配,如果|M|=r,|N|=s,s>r,那么M⊕N包含了至少s-r条匹配M的增长路径。
proof:
考虑一个图G’=(V,M⊕N),因为M和N均为匹配,那么每个顶点最多与M-N或N-M中的一条边相连,因此图G’的连通分支有下列三种情况:
(1)单悬挂点
(2)边含于M-N和N-M的一条回路
(3)边含于M-N和N-M的一条通路
设这些连通分支为C1,C2……,Cg,Ci = (Vi,Ei)
令α(Ci)= |Ei∩N| -|Ei∩M|则α(Ci)∈{-1,0,1}
当且仅当α(Ci)为1时Ci是M的一条增广路径
∑α(Ci)= |N-M|-|M-N|=|N|-|M|=s-r
因此共有至少s-r个连通分支使得α(Ci)=1,这些连通分支都是M的增广路径。
在这里插入图片描述
COROLLARY 1
当且仅当匹配M没有增广路径时M为最大匹配
COROLLARY 2
设|M|=r,最大匹配数为s,则存在一条M的长度<=2 ⌊r/(s-r)⌋+1 的增广路径
Proof
设N是一个最大匹配,则M⊕N包含了s-r条M的增广路径,这些路径一共最多包含r条M中的边,所以每一条路径最多包含最多 ⌊r/(s-r)⌋条M中的边,因此其最多有2 ⌊r/(s-r)⌋+1 条边
THEOREM 2
设M为一个匹配,P为M的一个最短增广路径,P’为P⊕M的一个增广路径,有
|P’|>= |P| +|P∩P’|

proof
令N = P⊕M⊕P’.则N为一个匹配且|N|=|M|+2,所以M⊕N包含两条M的增广路径(顶点不相交),令它们为P1,P2。因为M⊕N = P⊕P’,有 |P⊕P’|>=|P1|+|P2|.但因为P为M的最短增广路径,则|P1|>=|P|,|P2|>|P|.所以|P⊕P’|>=|P1|+|P2|>=2|P|.又因为
|P⊕P’|=|P|+|P‘|-|P∩P‘|,因此|P’|>= |P| +|P∩P’|
我们想象下列计算模式:从M0=空集开始,计算一个序列M0,M1,M2,……Mi,……,Pi是Mi的最短增广路径,Mi+1=Mi⊕Pi
COROLLARY 3
|Pi|<=|Pi+1|
COROLLARY 4
对于所有的i,j,如果|Pi|=|Pj|,则Pi和Pj是顶点不相交的
proof
反证法:设|Pi|=|Pj|,i<j,且顶点相交.那么存在k,l,使得i<=k<l<=j,Pk和Pl是顶点相交的,且对于每一个m,如果m满足k<m<l,则Pm和Pk,Pl都是顶点不相交的。那么Pl是Mk⊕Pk的一条增广路径,由定理二可知|P|>= |Pl| +|Pk∩Pl|。因为|Pk|=|Pl|,可得|Pk∩Pl|=0.由此可知Pk和Pl不包含相同的边。如果Pk和Pl包含相同的顶点v,则他们就会包含与之相关的Mk⊕Pk中的一条边,与事实不符。因此Pk和Pl是顶点不相交的,定理得证。
THEOREM 2
设最大匹配数为s,下列序列中不同整数的个数小于等于
在这里插入图片描述
|P0|,|P1|,……|Pi|……

proof

在这里插入图片描述
|Mr| = r由COROLLARY 2得
在这里插入图片描述
对于i<r,|Pi| 是小于等于
在这里插入图片描述
的一个正奇数,对于r<i<s,|Pi|是s-r个整数之一,故加起来总共是
在这里插入图片描述

三、算法描述

(1)对于一般图找最大匹配算法是从空集开始,找到M的所有最短增广路径(彼此顶点不相交),再将他们合并,重复执行,直至找不到增广路径为止。算法如下:
在这里插入图片描述

(2)对于二分图,有匈牙利算法
匈牙利算法不断为M寻找一条增广路径(不一定是最短增广路径),直至找不到增广路径为止,时间复杂度为O(n3)
以男女婚配为例
xi代表男生,yi代表女生
请添加图片描述
从x1开始寻找增广路径
请添加图片描述
接下来是x2
请添加图片描述
到x3时,有多条路径可选,依赖于邻接表的存储方式,这里我们以邻接矩阵为例,选取
(x3,y1)(y1,x1)(x1,y2)(y2,x2)(x2,y5
请添加图片描述
x4,x5,如法炮制最终得到一个最大匹配如图
请添加图片描述
匈牙利算法实现如下:

bool Graph::match(int i)
{
    for (int j = 1; j <= N; ++j)
        if (adj[i][j] && !known[j]) //有边且未访问
        {
            known[j] = true;                 //记录状态为访问过
            if (p[j] == 0 || match(p[j])) //如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
            {
                p[j] = i;    //当前左侧元素成为当前右侧元素的新匹配

                return true; //返回匹配成功
            }
        }
    return false; //循环结束,仍未找到匹配,返回匹配失败
}

int Graph::Hungarian()
{ 
    int cnt = 0;
    for(int i = 1; i <N+1; i++)
       p[i]=0;

    for (int i = 1; i <= M; i++)
    {
        for(int i = 1; i <N+1; i++)
            known[i]=false;
        if (match(i))
            cnt++;
    }

    return cnt;
}

(3)
现在我们讨论算法A在二分图里的实现,step1意在找到一个M的最短增广路径的集合。
为此,我们需要构造一个有向图,令E-M中的边从女生指向男生,M中的边从男生指向女生,即
在这里插入图片描述
接下来我们找到其一个子图使得从一个free的女生开始,到一个free的男生结束,其余顶点均在M中,这就是M的一条最短增广路径。
借鉴我们解决最大流问题时的想法,类似的解决最大匹配问题

如下图,偶数层为男生,奇数层为女生,所有的free的女生位于L0所有的free的男生位于
L5,s指向所有free的男生,所有的free的女生指向t,这样就把寻找最短增广路径的集合类比为一个最大流问题
在这里插入图片描述
下面列出算法B的伪码实现
在这里插入图片描述

该算法的时间复杂度为
在这里插入图片描述
下面是我自己的算法实现供大家学习参考

void Graph::initialize()
{
    int i;
    cin>>M>>N>>Ne;
    vector<string> adj1,adj2;
    G.insert(pair<string,vector<string>>("s",adj1));

    for(i = 0; i < Ne; i++)
    {
        string p1, p2;
        cin>>p1>>p2;
        if(G.find(p1)==G.end()) {
            vector<string> adj;
            G.insert(pair<string,vector<string>>(p1,adj));
            G[p1].push_back("t");

        }

        if(G.find(p2)==G.end()) {
            vector<string> adj;
            G.insert(pair<string,vector<string>>(p2,adj));
            G["s"].push_back(p2);
        }
        if(known.find(p1)==known.end()) {
            known.insert(pair<string,bool>(p1,false));
        }
        if(known.find(p2)==known.end()) {
            known.insert(pair<string,bool>(p2,false));
        }
        known.insert(pair<string,bool>("t",false));
        // G[p1].push_back(p2);
        G[p2].push_back(p1);
    }

}

int Graph::max_match()
{   
    stack<string> STACK;
    STACK.push("s");
    string u,v;
    string FIRST;
    vector<string> B;
    int cnt = 0;
    bool flag = false;
    while(!STACK.empty())
    {
        u = STACK.top();
        //当此条路径不通时返回上层节点
        if(G[u].empty()) {
            while(G[STACK.top()].empty()) STACK.pop();
        }

        while(!G[u].empty()){
            FIRST = G[u].back();
            // 该节点未曾遍历
            if(!known[FIRST]) {
                if(FIRST!="t") {
                    STACK.push(FIRST);
                    known[FIRST] = true;
                    G[u].pop_back();
                    break;
                }else if(FIRST=="t"){//找到增广路径,立即输出
                    G[STACK.top()].pop_back();
                    while(STACK.top()!="s") {
                        v = STACK.top();
                        // cout<<v<<" ";
                        STACK.pop();
                        if(STACK.top()!="s")
                            G[v].push_back(STACK.top());
                    }
                    // cout<<endl;
                    cnt++;
                    for(auto iter = known.begin();iter!=known.end();iter++) iter->second=false;
                    known["t"] = false;
                    break;

                }
            }else G[u].pop_back();
        }
        if(G[STACK.top()].empty()&&(STACK.top()=="s")) break;
    }
    return cnt;
}


该算法本质上是匈牙利算法的非递归实现,但其结合了最大算法的思想,用栈存储,一旦某条边不能构成增广路径,便即删去,是以更为精妙,运行时间更快

J.E.Hopcroft and R.E.Karp,“An n5/2Algorithm for Maximum Mathching in Biparartite Graphs”,SIAM Journal on Compuing,2(1973),135-158

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值