zoukankan      html  css  js  c++  java
  • 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算法

          第一遍读这个算法的时候。不懂,如今读这个算法,认为非常清晰,如今把算法的思路复述一遍。不知道第一次读的人会不会认为easy理解:

          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

          解决问题就非常easy了。我们先加入上源和目的地节点。如果是任务分配问题,则源能够有边指向全部人。全部任务有边能够指向目的地,我们要找的是人和任务之间的最优匹配。

    代码:

    // 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算法,这个后面再讲。此算法的时间复杂度和空间复杂度以及改进也能够探讨


     

  • 相关阅读:
    Loved
    什么是REST
    统一资源定位符URL(Uniform Resource Locator)
    HTTP工作原理
    系统程序员成长计划内存管理(一)
    系统程序员成长计划工程管理(四)
    系统程序员成长计划-内存管理(四)
    HTTP请求报文格式
    系统程序员成长计划内存管理(二)
    系统程序员成长计划-内存管理(三)
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7151773.html
Copyright © 2011-2022 走看看