Maximum Bipartite Matching


算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进。最后,再考虑时间和空间复杂度的问题。

了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maximum bipartite matching问题,解决这个问题,是通过Network Flow问题的解法来实现。

一、Network Flow

Network Flow,指的是在从source 到 destination的路径组成一个network, 每条边有一个capacity, 表示从这条边上能通过的最大信息流,而Network Flow问题则要找出从源到目的地能通过的最大流, Maximum Flow. 信息在流动的过程中需要遵循两个原则;

1. 对于每个节点,流入和流出的信息必须相等。

2.流过每条边的信息不能超过边上的capacity.

最大流问题和minimum cut是等价的,找最大流也就是找minimum cut,minimum cut是如下定义的:

我们要在Network上删除一些边,删除掉这些边后,从source 就没有路径到目的地了,我们要找到尽可能少的边,来达到这个目的,这就是minimum cut。

二、 Ford-Fulkerson算法

第一遍读这个算法的时候,不懂,现在读这个算法,觉得很清晰,现在把算法的思路复述一遍,不知道第一次读的人会不会觉得容易理解:

1、 构建Residual graph:由于在原network上已经有了capacity, 现在给定这个网络一个流flow的值, 例如边是(u,v)我们计算capacity-f, 同时我们也计算(v,u),值为f(因为capacity为0),

如果一条边的这个值为正,则保留,否则删除。

2、augmenting path: 通过1得到的就是Residual graph,这个graph上的从source到destination的所有路径都叫做augmenting path.

3、针对每条augmenting path: 改变path上所有边的capacity,改变规则如下(以(u,v)为例):

找到这条path上的最小的capacity, f,

减少u->v的capacity, 增加v->u的capacity.

算法的时间复杂度 O(m+n)f),f是max-flow.

代码:

// C++ program for implementation of Ford Fulkerson algorithm
#include <iostream>
#include <limits.h>
#include <string.h>
#include <queue>

using namespace std;

// Number of vertices in given graph
#define V 6

/* Returns true if there is a path from source ‘s‘ to sink ‘t‘ in
  residual graph. Also fills parent[] to store the path */
bool bfs(int rGraph[V][V], int s, int t, int parent[])
{
    // Create a visited array and mark all vertices as not visited
    bool visited[V];
    memset(visited, 0, sizeof(visited));

    // Create a queue, enqueue source vertex and mark source vertex
    // as visited
    queue <int> q;
    q.push(s);
    visited[s] = true;
    parent[s] = -1;

    // Standard BFS Loop
    while (!q.empty())
    {
        int u = q.front();
        q.pop();

        for (int v=0; v<V; v++)
        {
            if (visited[v]==false && rGraph[u][v] > 0)
            {
                q.push(v);
                parent[v] = u;
                visited[v] = true;
            }
        }
    }

    // If we reached sink in BFS starting from source, then return
    // true, else false
    return (visited[t] == true);
}

// Returns tne maximum flow from s to t in the given graph
int fordFulkerson(int graph[V][V], int s, int t)
{
    int u, v;

    // Create a residual graph and fill the residual graph with
    // given capacities in the original graph as residual capacities
    // in residual graph
    int rGraph[V][V]; // Residual graph where rGraph[i][j] indicates
                     // residual capacity of edge from i to j (if there
                     // is an edge. If rGraph[i][j] is 0, then there is not)
    for (u = 0; u < V; u++)
        for (v = 0; v < V; v++)
             rGraph[u][v] = graph[u][v];

    int parent[V];  // This array is filled by BFS and to store path

    int max_flow = 0;  // There is no flow initially

    // Augment the flow while tere is path from source to sink
    while (bfs(rGraph, s, t, parent))
    {
        // Find minimum residual capacity of the edhes along the
        // path filled by BFS. Or we can say find the maximum flow
        // through the path found.
        int path_flow = INT_MAX;
        for (v=t; v!=s; v=parent[v])
        {
            u = parent[v];
            path_flow = min(path_flow, rGraph[u][v]);
        }

        // update residual capacities of the edges and reverse edges
        // along the path
        for (v=t; v != s; v=parent[v])
        {
            u = parent[v];
            rGraph[u][v] -= path_flow;
            rGraph[v][u] += path_flow;
        }

        // Add path flow to overall flow
        max_flow += path_flow;
    }

    // Return the overall flow
    return max_flow;
}

// Driver program to test above functions
int main()
{
    // Let us create a graph shown in the above example
    int graph[V][V] = { {0, 16, 13, 0, 0, 0},
                        {0, 0, 10, 12, 0, 0},
                        {0, 4, 0, 0, 14, 0},
                        {0, 0, 9, 0, 0, 20},
                        {0, 0, 0, 7, 0, 4},
                        {0, 0, 0, 0, 0, 0}
                      };

    cout << "The maximum possible flow is " << fordFulkerson(graph, 0, 5);

    return 0;
}

三、Maximum Bipartite Matching

解决这个问题就很简单了,我们先添加上源和目的地节点,假设是任务分配问题,则源可以有边指向所有人,所有任务有边可以指向目的地,我们要找的是人和任务之间的最优匹配。

代码:

// 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])
        {
            seen[v] = true; // Mark v as visited

            // 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));

    int result = 0; // Count of jobs assigned to applicants
    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 program to test above functions
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;
}

四、对于任务分配问题,还有Hungrian算法,这个后面再讲,此算法的时间复杂度和空间复杂度以及改进也可以探讨

Maximum Bipartite Matching,布布扣,bubuko.com

时间: 2024-12-25 16:55:43

Maximum Bipartite Matching的相关文章

Maximum Cardinality Bipartite Matching: Augmenting Path Algorithm

http://www.csie.ntnu.edu.tw/~u91029/Matching.html 1 int nx,ny; 2 int mx[N],my[N]; 3 bool vy[N]; 4 bool g[N][N]; 5 6 int decode(int x,int y) {return x*15+y;} 7 8 bool dfs(int x) 9 { 10 for(int y=0;y<ny;++y) 11 if(g[x][y] && !vy[y]) 12 { 13 vy[y]

POSIX 线程详解(2-线程创建和销毁)

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

C++ Primer 学习笔记_66_面向对象编程 --定义基类和派生类[续]

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

HDOJ-1052 田忌赛马

田忌赛马 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述: Here is a famous story in Chinese history. "That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others." "Both of T

C语言贪心(2)___田忌赛马(Hdu 1052)

Problem Description Here is a famous story in Chinese history. "That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others." "Both of Tian and the king have t

SDAU课程练习--problemC

题目描述 Here is a famous story in Chinese history. "That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others." "Both of Tian and the king have three horses in

General Problem Solving Techniques [Intermediate-1]~F - Tian Ji -- The Horse Racing

Here is a famous story in Chinese history. That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others. Both of Tian and the king have three horses in different classes,

HDU1052Tian Ji -- The Horse Racing

Tian Ji -- The Horse Racing Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 26683    Accepted Submission(s): 7875 Problem Description Here is a famous story in Chinese history. "That was about 2

IOI 2009:Mecho

IOI2009 Mecho Time Limit: 10000ms Memory Limit: 262144KB This problem will be judged on SPOJ. Original ID: CTOI09_164-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-SAT