图论——入门级二分图最大匹配Bipartite Matching

1.问题描述

 我们先来了解一下相关图论的概念:

  • 二分图:又称二部图。是图论中的一种特殊模型。设G=(V,E)是一个无向图,如果结点集V可分割为两个互不相交的子集(V1,V2),并且图中的每条边(i,j),所关联的两个结点i和j分别属于这两个不同的结点集,则称G为一个二分图。
  • 匹配:在图论中,一个匹配(matching)是一个边的集合,其中任意两条边都没有公共结点。如图所示就算是一个匹配:

  • 最大匹配(maximum matching):一个图的所有匹配中,边数最多匹配称为这个图的最大匹配。

求二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm),但目前我们先不用这两种方法来实现求解二分图最大匹配,而是用简单的dfs实现:

现在有这么有个例子:

A同学和伙伴们去游乐场玩,准备要坐过山车。过山车一排有两个座位,保险起见,每个女生必须与一个男生坐在一起。但是,每个人都希望和自己认识的人坐在一起。比如说,1号女生和1号男生认识,因此他们想坐在一起。另外1号女生与2号男生也互相认识,因此他们也可以坐在一起。像这样的关系还有:2号女生认识2号和3号的男生,3号男生认识1号男生。请问怎么安排座位才能让最多的人满意呢?这就是典型简单的二分图最大匹配。

2.算法设计 

  1.  首先从任意一个未被匹配的点u开始,从点u的边中任意选一条边(假设这条便是u->v)开始配对。如果此时点v还没有被配对,则配对成功,此时便找到了一条增广路(只不过这条增广路比较简单)。如果此时v已经被配对,那就要尝试进行“连锁反应”。如果尝试成功了,则找到一条增广路,此时需要更新原来的配对关系。这里要用一个数组match来记录配对关系,比如点v与点u配对了,救济做match[v]=u。配对成功后,记得将配对数+1.配对的过程我们可以通过深度优先搜索来实现。
  2. 如果刚才所选的边配对失败,要从点u的边中再重新选一条边,进行尝试,直到u配对成果,或者尝试过点u所有的边为止。
  3. 接下来继续对剩下没有配对的点一一进行配对,直到所有的点都尝试完毕,找不到新的增广路为止。
  4. 输出配对数。

3.源代码 

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=111;
int e[N][N];//存储边
int matched[N];//判断是否已经匹配
int marked[N]; //判断是否已经访问
int n;
int m;

int dfs(int u)
{
    for(int i=1;i<=n;i++)
    {
        if(marked[i]==0 && e[u][i]==1) //没有被访问过且可达
        {
            marked[i]=1;
            if(matched[i]==0 || dfs(matched[i])) //如果没有配对,将i与u配对。
                                                                //用dfs遍历一编i,看有没有配对。如果没有配对,返回0
            {
                matched[i]=u;
                return 1;//配对成功返回1.不用进行dfs
            }
        }
    }
    return 0;
}

int main()
{
    int edge1;
    int edge2;
    int sum=0;
    cout << "请输入n个顶点m条边" << endl;
    cin >> n >> m;
    cout << "请输入所有可进行配对的顶点: "<< endl;
    for (int i=1;i<=m;i++)
    {
        cin >> edge1 >> edge2;
        e[edge1][edge2]=1;//说明这两个顶点之间存在边。
    }
    //初始化匹配数组。
    for (int i=1;i<=n;i++)
    {
        matched[i]=0;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            marked[j]=0; //清空上次搜索访问过的记录
        }
        if(dfs(i))
        {
            sum++;
        }
    }
    cout << "最大匹配数是:"<< endl;
    cout << sum << endl;
    return 0;
}

4.测试结果 

 

发布了57 篇原创文章 · 获赞 9 · 访问量 3615

猜你喜欢

转载自blog.csdn.net/Jayphone17/article/details/102859956