两个矩阵是否相交的算法_网络流算法学习笔记2(简洁易懂)—二分图匹配求解算法、代码及算法设计一书关于二分图匹配章节的翻译...

二分图匹配

匹配问题有多种形式。例如,五个求职者面试五个职位。每个求职者都已经列出了自己能胜任的职位,现在的任务就是尽可能多地将工作分配给求职者,但是每份工作只能提供给一位求职者。

Ford- Fulkerson算法也可以用于解決二分图匹紀问题。在计算机科学中,这种方法称为“问题归约”。我们将二分图匹配问题归约为在流网络上的最大流问題,下面我们会介绍如何将二分图匹配问題的输入映射成最大流问题的输入,以及如何将最大流问题的输出映射成二分图匹配问题的输出。

1.1输入/输出

二分图匹配包含一个n个元素的集合,其中每个元素

∈S;输人还包含一个m位参与者的集合,其中每位参与者
∈T;最后还有P对匹配,其中每对匹配
∈P。P中的每对匹配将元素
∈S和参与者
∈T关联起来。集合S和集合T是不相交的,这就是二分图匹配名字的由来。

输出是从P中可接受的原始匹配中挑选的匹配集合(

),这些区配代表了能够匹配上的最大配对数目。该算法保证不存在更多的匹配数(虽然有可能会有其他匹配方式能够得到同样多的匹配数目)。

1.2解决方案

我们将二分图匹配问题归约成最大流问题,而并没有重新设计一个新的算法。在二分图匹配中,从元素集S和参与者集合T中选择了匹配(

)之后,
或者
就不能被其他匹配选择。我们使用n表示集合S的大小,m表示集合T的大小。为了在流网络图中产生同样的行为,我们可以按照如下方法来构建图G=(V,E):

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所示

263b36871333582b6081f2cd9529e933.png

其中1号顶点对应a,4号顶点对应x,以此类推。我们可以进一步改善解决方案来选择三对匹配:(a,x)、(c,y)和(b,x)。在找到一条增广路<0,3,5,2,4,7>之后,该算法会对流网络进行相应的调整。应用增广路径会移除匹配(b,y),并添加匹配(b,x)和(c,y)。

feaf22cce04258234acaa4d4390d3d68.png
如何实施上述方法?
让我们首先定义输入和输出形式。输入采用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

b2355d26eff9d6caf42c37e76b0409d9.png
Dancing problem

055b71e1ae0e85a85dfacf5619ba234d.png

答案:是的!

cfdea8f1df3769ac2e930c18f0beffad.png

------------------------------------------------------------------------------

算法设计一书关于二分图匹配章节的翻译

在为最大流问题开发了一组强大的算法之后,我们现在开始为最大流和最小割的应用开发相应算法。我们从两个非常基础的应用开始。首先,在这一节中,我们讨论了本章开头提到的二分匹配问题。而下一节我们将讨论更一般的不相交路径问题。

问题描述

我们开发最大流问题的最初目标之一是能够解决二分匹配问题,我们现在展示了如何做到这一点。二分图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。

93595e62c22df26edf1dd84fb938e3b9.png

我们现在计算这个网络G中的最大s-t流。我们将发现,这个最大值等于G中最大匹配的大小,而且,我们的分析将显示如何能够使用流本身恢复匹配。

算法分析

0d9bf47336c156abceace704d70757a4.png

反之,假设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中的匹配非常简单。对于这个流问题,我们有

c901915bb1e59c1fd48175d30500babe.png

.因此,通过使用(7.5)中的O(MC)界,我们得到以下结果。(7.38)利用Ford-Fulkerson算法可以在O(mn)时间内找到二分图的最大匹配。

有趣的是,如果我们使用前几节中研究出的“更好的”界O(

)或O(
),对这个问题我们将得到更不好地运行时间O(
)或O(
).这没有什么矛盾的(即使C相对于m和n非常大,这些bounds也可以被设计成对所有实例都表现很好。)但是对于二分匹配问题,C=n,因此我们不需要这样多此一举(付出额外复杂的代价)。

在G'中考虑增广路径( the augmenting paths)是很有价值的。考虑图7.10中由边(

)组成的匹配M

8e6eb2a34d49485e9b1719eba8f2a494.png

设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,设

⊆Y表示所有节点和A中节点adjacent的节点集合。如果图有一个完美匹配,那么A中每个节点都必须匹配到
中不同节点,所以
必须至少和A一样大,这给了我们以下事实。
(7.39)如果二元图G=(V,E)有一个完全匹配,那么对于所有的A⊆X,我们都必须有

这说明如果某子集A⊆X满足

,则不存在完美匹配。.但(7.39)反过来也是真的吗?答案在|X|=|Y|的情况下是肯定的,并且现在有研究人员提供了一种在多项式时间O(mn)内找到子集A的方法。

证明如下:

0ca65371a29c7663024fc96bd75be6dd.png

eadc74ad29a6e4d15db0b14f1fcc8358.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值