二分图匹配
匹配问题有多种形式。例如,五个求职者面试五个职位。每个求职者都已经列出了自己能胜任的职位,现在的任务就是尽可能多地将工作分配给求职者,但是每份工作只能提供给一位求职者。
Ford- Fulkerson算法也可以用于解決二分图匹紀问题。在计算机科学中,这种方法称为“问题归约”。我们将二分图匹配问题归约为在流网络上的最大流问題,下面我们会介绍如何将二分图匹配问題的输入映射成最大流问题的输入,以及如何将最大流问题的输出映射成二分图匹配问题的输出。
1.1输入/输出
二分图匹配包含一个n个元素的集合,其中每个元素
输出是从P中可接受的原始匹配中挑选的匹配集合(
1.2解决方案
我们将二分图匹配问题归约成最大流问题,而并没有重新设计一个新的算法。在二分图匹配中,从元素集S和参与者集合T中选择了匹配(
V包含n+m+2个顶点
每个元素映射为编号为i的顶点,每个参与者映射为编号为n+j的顶点。创建一个新的源顶点src(编号为0)和一个新的汇点tgt(编号为n+m+1)
E包含n+m+k条边
在新的src顶点到从S映射过来的顶点之间添加n条边,并在新的tgt顶点到从T映射过来的顶点之间添加m条边,对于k对匹配中的每一对匹配,分,添加边(i,n+j)。所有边的容量都必须为1。
在流网络图G中计算出的最大流就是原始二分图匹配问题中的最大匹配集合,这已经过证明( Cormen等,2009)。例如,考虑图a中两对匹配(a,z)和(b,y)组成的最大匹配集合,依照上述过程构造的流网络如图b所示
其中1号顶点对应a,4号顶点对应x,以此类推。我们可以进一步改善解决方案来选择三对匹配:(a,x)、(c,y)和(b,x)。在找到一条增广路<0,3,5,2,4,7>之后,该算法会对流网络进行相应的调整。应用增广路径会移除匹配(b,y),并添加匹配(b,x)和(c,y)。
如何实施上述方法?
让我们首先定义输入和输出形式。输入采用Edmonds矩阵的形式,该矩阵是一个二维数组'bpGraph [M] [N]',具有M行(对于M个求职者)和N列(对于N个工作)。如果第i个申请人对第j个工作感兴趣,则bpGraph [i] [j]的值为1,否则为0。
输出是可以找到工作的最大人数。
一种简单的实现方法是创建一个矩阵,该矩阵表示具有M + N + 2个顶点的有向图的邻接矩阵表示。调用fordFulkerson()作为矩阵。此实现需要O((M + N)*(M + N))额外空间。
使用图是二部图并且每个边的容量为0或1的事实,可以减少额外的空间,并可以简化代码。这个想法是使用DFS遍历为申请人找到工作(类似于增广路径))。我们为每个求职者调用bpm(),bpm()是基于DFS的函数,它尝试所有可能的方法来为求职者分配工作。
在bpm()中,我们一步一步地尝试申请人'u'感兴趣的所有工作,直到找到工作,或者所有工作都不招'u'。对于我们尝试的每一项工作,我们都会遵循:
如果未将工作分配给任何人,我们只需将其分配给申请人并返回true。如果一个工作被分配给其他人,比如说x,那么我们递归检查x是否可以被分配给其他工作。为了确保x不会再次获得相同的工作,我们在对x进行递归调用之前将工作“ v”标记为已被分配。如果x可以找到其他工作,bpm()就返回true。我们使用数组maxR [0..N-1]来存储分配给不同职位的申请人。
如果bmp()返回true,则流网络中存在一个增广路径,我们将1个流单位添加到maxBPM()的结果中。
// A C++ program to find maximal
// Bipartite matching.
#include <iostream>
#include <string.h>
using namespace std;
// M is number of applicants
// and N is number of jobs
#define M 6
#define N 6
// A DFS based recursive function
// that returns true if a matching
// for vertex u is possible
bool bpm(bool bpGraph[M][N], int u,
bool seen[], int matchR[])
{
// Try every job one by one
for (int v = 0; v < N; v++)
{
// If applicant u is interested in
// job v and v is not visited
if (bpGraph[u][v] && !seen[v])
{
// Mark v as visited
seen[v] = true;
// If job 'v' is not assigned to an
// applicant OR previously assigned
// applicant for job v (which is matchR[v])
// has an alternate job available.
// Since v is marked as visited in
// the above line, matchR[v] in the following
// recursive call will not get job 'v' again
if (matchR[v] < 0 || bpm(bpGraph, matchR[v],seen, matchR))
{
matchR[v] = u;
return true;
}
}
}
return false;
}
// Returns maximum number
// of matching from M to N
int maxBPM(bool bpGraph[M][N])
{
// An array to keep track of the
// applicants assigned to jobs.
// The value of matchR[i] is the
// applicant number assigned to job i,
// the value -1 indicates nobody is
// assigned.
int matchR[N];
// Initially all jobs are available
memset(matchR, -1, sizeof(matchR));
// Count of jobs assigned to applicants
int result = 0;
for (int u = 0; u < M; u++)
{
// Mark all jobs as not seen
// for next applicant.
bool seen[N];
memset(seen, 0, sizeof(seen));
// Find if the applicant 'u' can get a job
if (bpm(bpGraph, u, seen, matchR))
result++;
}
return result;
}
// Driver Code
int main()
{
// Let us create a bpGraph
// shown in the above example
bool bpGraph[M][N] = {{0, 1, 1, 0, 0, 0},
{1, 0, 0, 1, 0, 0},
{0, 0, 1, 0, 0, 0},
{0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1}};
cout << "Maximum number of applicants that can get job is "
<< maxBPM(bpGraph);
return 0;
}
Output :
Maximum number of applicants that can get job is 5
时间复杂度
Theorem. The Ford-Fulkerson algorithm solves the bipartite matching problem in
O(m n) time.
Theorem. [Hopcroft-Karp 1973] The bipartite matching problem can be solved in
O(m n^1/2) time.
非二分图匹配
Nonbipartite matching. Given an undirected graph (not necessarily bipartite), find a
matching of maximum cardinality.
・Structure of nonbipartite graphs is more complicated.
・But well-understood.
・Blossom algorithm: O(n^4)
・Best known: O(m n1/2)
k-regular bipartite graph
答案:是的!
------------------------------------------------------------------------------
算法设计一书关于二分图匹配章节的翻译
在为最大流问题开发了一组强大的算法之后,我们现在开始为最大流和最小割的应用开发相应算法。我们从两个非常基础的应用开始。首先,在这一节中,我们讨论了本章开头提到的二分匹配问题。而下一节我们将讨论更一般的不相交路径问题。
问题描述
我们开发最大流问题的最初目标之一是能够解决二分匹配问题,我们现在展示了如何做到这一点。二分图G=(V,E)是 节点集可以划分为V=X∪Y的未定向图,其属性为每条边e∈E在X中具有一端,另一端则位于Y。G中的匹配M是满足如下条件的E的子集:每个节点出现在M中的至多一个边中。二分匹配问题的目标是寻找size最大的M。
定义匹配问题的图是无向的,而流网络是有向的;但是,对于最大流问题,使用一种算法来寻找最大匹配并不困难。
从二分匹配问题的一个例子开始,我们构造了一个流网络G,如图7.9所示。首先,我们将G中的所有边从X指向Y,然后添加节点s、t,最后令G中每条边容量为1。
我们现在计算这个网络G中的最大s-t流。我们将发现,这个最大值等于G中最大匹配的大小,而且,我们的分析将显示如何能够使用流本身恢复匹配。
算法分析
反之,假设G'中有一个值为k的流f',利用最大流的integrality定理,存在一个整数值为k的流f。现在,考虑流量为1的(x,y)边的集合M。这里有三个关于集合M的简单事实:
(7.34)M'包含k条边。
证明:考虑G中的割(A,B),其中A={s}∪X。流值是离开A的总流量减去进入A的总流量。 其中第一项就是M的size,第二项是0。因此,M包含k个边。
(7.35)X中的每个节点都最多是M中一条边的末端。
证明:假设x是m中至少两条边的末端。由于流量是整数值,这意味着至少两个流量单位从x离开。通过流量守恒,至少有两个单位流量必须进入x,但这是不可能的,因为事实上进入x的容量为1。因此,x最多是M中一条边的末端。
(7.36)同理,Y中的每个节点都最多是M中一条边的初始端点。
(7.37)G中最大匹配的大小等于G'中最大流的值,而G中最大匹配的边是G'中从X到Y的边。
Bounding the Running Time
现在,让我们考虑一下在G中计算最大匹配的速度。令n=|X|=|Y|,我们假定原问题中的每个节点至少是一条边的端点,因此m≥n/2。计算最大匹配的时间主要是计算G'中整数值最大流的时间,因为将其转换为G中的匹配非常简单。对于这个流问题,我们有
.因此,通过使用(7.5)中的O(MC)界,我们得到以下结果。(7.38)利用Ford-Fulkerson算法可以在O(mn)时间内找到二分图的最大匹配。
有趣的是,如果我们使用前几节中研究出的“更好的”界O(
在G'中考虑增广路径( the augmenting paths)是很有价值的。考虑图7.10中由边(
设f是G中对应的流。这种匹配不是最大的,所以f不是最大s-t流,因此在残差图G',f中有一个增广路径。在图7.10(b)中标记就标记了一条这样的增广路径。注意,边(x2,y2)和(x3,y3)是后向的,所有其他边都是前向。所有增广路径必须在前后向边间交替,就像G'中从X到Y的所有边一样。 因此,在寻找最大匹配的情境中,增广路径也称为交替路径。这种增广的效果是将后向边从匹配中去除,并用前向边替换它们。由于增广路径从s到t,比后向边多一个前向边,因此匹配的大小增加了一个。
拓展:无完全匹配二分图的结构
算法上,我们已经看到了如何找到完美匹配:我们使用上面的算法找到最大匹配,然后检查这种匹配是否完美。
但让我们问一个稍微少一点的算法问题。不是所有的二部图都有完美的匹配。没有完美匹配的二部图是什么样子的?有什么简单的certifificate可以帮助我们判定吗? 我们可以通过检查相关图G'中的最大流是否具有至少N值来决定图G是否具有完美匹配。 由最大流最小割定理,如果G'中的最大流小于N,则将有一个S-T容量小于N。因此在某种程度上,容量小于n的割集提供了这样的certifificate。然而,我们想要一个在原始图G上具有自然意义的certifificate。
这样的certifificate会是什么样子?例如,如果节点x1,x2∈X各只有一条入射边,而每条边的另一端是相同的节点y,那么很明显,这个图没有完美的匹配:x1和x2都需要匹配到同一个节点y。更一般地,考虑节点的子集A⊆X,设
这说明如果某子集A⊆X满足
证明如下: