二分图最大匹配(匈牙利算法)

首先是二分图定义与判断  http://www.cnblogs.com/wenruo/p/5243034.html

给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不连接同一个顶点,则称M是一个匹配。

一个很好的比喻是,一个二分图,左边代表男生,右边代表女生,连线代表有好感,匹配就是把他们互相有好感的撮合起来。当然,要一夫一妻!

所谓最大匹配,就是撮合最多对。(写下这句话的时候,我在图书馆,旁边有一对情侣在虐狗……)

完美匹配就是所有的人都不落单。

上图红线代表一个匹配,图中的每条红线的就代表一对匹配的情侣们。有个汉纸落单了,所以这个不是完美匹配。

 

好了,回归正题,不要再虐我,哦不,虐狗了。。。

我们把上图已经匹配的点叫做匹配点,没匹配的点叫未匹配点。红线叫做匹配边,其余叫做非匹配边。

介绍几个概念(表述未必标准):

假设我们已经找到了图G的一个匹配M

交替路:图中的一条路径,交替经过匹配边,非匹配边,则称为该路径为交替路。

增广路:从一个未匹配点出发,走交替路,如果到达另一个未匹配点,则这条交替路称为增广路。

增广路一定有奇数条边,而且非匹配边比匹配边多一条。(动手画一下很容易得知,如下图左边是一个匹配 路径5->d->1->b就是一个增广路)

那么对于一个匹配,如果我们能够找到一条增广路,然后把匹配边变成非匹配边,非匹配边变成匹配边,就一定可以多出一个匹配。

于是找最大匹配的过程就变成了找增广路,当找不到增广路的时候,该匹配就是一个最大匹配。(即匈牙利算法,证明略。)

一个简单的DFS版的模板(详细注释),用vector存边。时间复杂度O(NE)

const int N = 505;

vector<int> G[N];   // vector存图
int n, m;
int match[N];       // 记录每个点匹配的对象 没匹配的为-1
bool used[N];       // 保证每一次找增广路的时候每个点仅搜索一次

bool findPath(int u)
{   // 从u点开始走交替路径 直到找到一个未匹配点
    for (unsigned i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if (!used[v]) { // 和该点连接且没有被搜索过的点
            used[v] = true;
            // 该点是未匹配点  或者 走过一条匹配边然后能找到未匹配点
            // 这里 设c=match[v] 既然c与match匹配了 那么c再走到其他任何一点经过的边都是非匹配边 也就实现了走交替路
            if (match[v] == -1 || findPath(match[v])) {
                // 找到一条增广路 把新匹配的点连起来
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

int maxMatch()
{
    int ans = 0;
    memset(match, -1, sizeof match);
    for (int i = 0; i < n; ++i) {   // 这里 二分图有两部分点 只需要搜索其中一部分就可以
            memset(used, false, sizeof used);
            used[i] = true;
            if (findPath(i)) ++ans; // 找到一条增广路 即多了一个匹配
    }
    return ans;
}

 

这里使用vector存的边,有些题解用矩阵存边G[a][b]表示X集合a点到Y集合b点的连线。a,b都是从1开始的。这里是不同的,因此用了同一个used和match来保存结果,所有的点都没有重复。

 

最大匹配的一些相关性质:

(1)二分图的最小顶点覆盖:用最少的点,让每条边都至少和其中一个点关联。
     最大匹配数 = 最小点覆盖数(Konig 定理)

(2)二分图的最大独立集:在图中选取最多的点,使任意所选两点均不相连
     最大独立数顶点数 — 最大匹配数

(3)最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
     最小路径覆盖数 = 顶点数 - 最大匹配数

 

posted @ 2016-03-04 19:49  我不吃饼干呀  阅读(1890)  评论(0编辑  收藏  举报