📜  最大二分匹配

📅  最后修改于: 2022-05-13 01:57:53.985000             🧑  作者: Mango

最大二分匹配

二分图中的匹配是一组以没有两条边共享端点的方式选择的边。最大匹配是最大尺寸(最大边数)的匹配。在最大匹配中,如果添加任何边,则不再是匹配。对于给定的二分图,可以有多个最大匹配。
我们为什么关心?
有许多现实世界的问题可以形成为二分匹配。例如,考虑以下问题:
有 M 个求职者和 N 个工作。每个申请人都有他/她感兴趣的工作子集。每个职位空缺只能接受一个申请人,一个职位申请人只能被任命为一个职位。为申请人找到工作分配,以便尽可能多的申请人获得工作。

最大匹配1

我们强烈建议您先阅读以下帖子。
最大流量问题的 Ford-Fulkerson 算法
最大二分匹配和最大流量问题
Maximum B ipartite Matching ( MBP ) 问题可以通过将其转换为流网络来解决(请参阅此视频以了解我们是如何得出这个结论的)。以下是步骤。

最大匹配2

1) 构建流网络
流网络中必须有源和汇。因此,我们添加一个源并将源的边添加到所有申请人。同样,将所有作业的边添加到接收器。每条边的容量标记为 1 个单位。

最大匹配2

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