最大二分匹配
二分图中的匹配是一组以没有两条边共享端点的方式选择的边。最大匹配是最大尺寸(最大边数)的匹配。在最大匹配中,如果添加任何边,则不再是匹配。对于给定的二分图,可以有多个最大匹配。
我们为什么关心?
有许多现实世界的问题可以形成为二分匹配。例如,考虑以下问题:
有 M 个求职者和 N 个工作。每个申请人都有他/她感兴趣的工作子集。每个职位空缺只能接受一个申请人,一个职位申请人只能被任命为一个职位。为申请人找到工作分配,以便尽可能多的申请人获得工作。
我们强烈建议您先阅读以下帖子。
最大流量问题的 Ford-Fulkerson 算法
最大二分匹配和最大流量问题
Maximum B ipartite Matching ( MBP ) 问题可以通过将其转换为流网络来解决(请参阅此视频以了解我们是如何得出这个结论的)。以下是步骤。
1) 构建流网络
流网络中必须有源和汇。因此,我们添加一个源并将源的边添加到所有申请人。同样,将所有作业的边添加到接收器。每条边的容量标记为 1 个单位。
2) 找到最大流量。
我们使用 Ford-Fulkerson 算法在步骤 1 中构建的流网络中找到最大流。最大流实际上就是我们要寻找的 MBP。
如何实现上述方法?
让我们首先定义输入和输出形式。输入采用 Edmonds 矩阵的形式,它是一个 2D 数组 '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' 感兴趣的工作,直到我们找到工作,或者所有工作都尝试了但没有运气。对于我们尝试的每一项工作,我们都会遵循。
如果工作没有分配给任何人,我们只需将其分配给申请人并返回 true。如果一个工作被分配给别人说 x,那么我们递归地检查 x 是否可以分配给其他工作。为了确保 x 不会再次获得相同的工作,我们在递归调用 x 之前将工作标记为“v”。如果 x 可以得到其他工作,我们更改工作 'v' 的申请人并返回 true。我们使用一个数组 maxR[0..N-1] 来存储分配给不同工作的申请人。
如果 bmp() 返回 true,则表示流网络中存在增广路径,并且在 maxBPM() 中将 1 个单位的流添加到结果中。
C++
// A C++ program to find maximal
// Bipartite matching.
#include
#include
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;
}
Java
// A Java program to find maximal
// Bipartite matching.
import java.util.*;
import java.lang.*;
import java.io.*;
class GFG
{
// M is number of applicants
// and N is number of jobs
static final int M = 6;
static final int N = 6;
// A DFS based recursive function that
// returns true if a matching for
// vertex u is possible
boolean bpm(boolean bpGraph[][], int u,
boolean 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(boolean bpGraph[][])
{
// 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[] = new int[N];
// Initially all jobs are available
for(int i = 0; i < N; ++i)
matchR[i] = -1;
// 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.
boolean seen[] =new boolean[N] ;
for(int i = 0; i < N; ++i)
seen[i] = false;
// Find if the applicant 'u' can get a job
if (bpm(bpGraph, u, seen, matchR))
result++;
}
return result;
}
// Driver Code
public static void main (String[] args)
throws java.lang.Exception
{
// Let us create a bpGraph shown
// in the above example
boolean bpGraph[][] = new boolean[][]{
{false, true, true,
false, false, false},
{true, false, false,
true, false, false},
{false, false, true,
false, false, false},
{false, false, true,
true, false, false},
{false, false, false,
false, false, false},
{false, false, false,
false, false, true}};
GFG m = new GFG();
System.out.println( "Maximum number of applicants that can"+
" get job is "+m.maxBPM(bpGraph));
}
}
Python3
# Python program to find
# maximal Bipartite matching.
class GFG:
def __init__(self,graph):
# residual graph
self.graph = graph
self.ppl = len(graph)
self.jobs = len(graph[0])
# A DFS based recursive function
# that returns true if a matching
# for vertex u is possible
def bpm(self, u, matchR, seen):
# Try every job one by one
for v in range(self.jobs):
# If applicant u is interested
# in job v and v is not seen
if self.graph[u][v] and seen[v] == False:
# 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] == -1 or self.bpm(matchR[v],
matchR, seen):
matchR[v] = u
return True
return False
# Returns maximum number of matching
def maxBPM(self):
'''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.'''
matchR = [-1] * self.jobs
# Count of jobs assigned to applicants
result = 0
for i in range(self.ppl):
# Mark all jobs as not seen for next applicant.
seen = [False] * self.jobs
# Find if the applicant 'u' can get a job
if self.bpm(i, matchR, seen):
result += 1
return result
bpGraph =[[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]]
g = GFG(bpGraph)
print ("Maximum number of applicants that can get job is %d " % g.maxBPM())
# This code is contributed by Neelam Yadav
C#
// A C# program to find maximal
// Bipartite matching.
using System;
class GFG
{
// M is number of applicants
// and N is number of jobs
static int M = 6;
static int N = 6;
// A DFS based recursive function
// that returns true if a matching
// for vertex u is possible
bool bpm(bool [,]bpGraph, 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)
{
// 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 = new int[N];
// Initially all jobs are available
for(int i = 0; i < N; ++i)
matchR[i] = -1;
// 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 = new bool[N] ;
for(int i = 0; i < N; ++i)
seen[i] = false;
// Find if the applicant
// 'u' can get a job
if (bpm(bpGraph, u, seen, matchR))
result++;
}
return result;
}
// Driver Code
public static void Main ()
{
// Let us create a bpGraph shown
// in the above example
bool [,]bpGraph = new bool[,]
{{false, true, true,
false, false, false},
{true, false, false,
true, false, false},
{false, false, true,
false, false, false},
{false, false, true,
true, false, false},
{false, false, false,
false, false, false},
{false, false, false,
false, false, true}};
GFG m = new GFG();
Console.Write( "Maximum number of applicants that can"+
" get job is "+m.maxBPM(bpGraph));
}
}
//This code is contributed by nitin mittal.
PHP
Javascript
输出:
Maximum number of applicants that can get job is 5