图论——二分图最大匹配与匈牙利算法

图论——二分图最大匹配与匈牙利算法

相关概念与术语

二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 U U U V V V ,使得每一条边都分别连接 U U U V V V 中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是: 不含有「含奇数条边的环」的图

二分图

  • 匹配:在图论中,一个 匹配 是一个边的集合,其中任意两条边都没有公共顶点。我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。匹配点和匹配边就是已经有匹配边相连的点,其他都是未匹配点和非匹配边。
  • 极大匹配:给出一个现有的匹配,如果在不修改现有匹配边的情况下,如果不能再增加匹配边,那么这个匹配就是一个 极大匹配
  • 最大匹配:一个图所有极大匹配中,所含匹配边数最多的极大匹配,称为这个图的 最大匹配 。在最大匹配中,匹配边的数量称为这个图的 最大匹配数
  • 完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个 完美匹配 。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。

匈牙利算法

求解二分图最大匹配问题的一个最常用的算法是匈牙利算法。下面介绍匈牙利算法的原理和使用。

  • 交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫 交替路
  • 增广路:从一个未匹配点出发,走交替路,如果终点是另一个未匹配点(出发的点不算),则这条交替路称为 增广路 。并且增广路的长度一定是奇数,按照次序给增光路的边编号,那么奇数编号的边是未匹配边,偶数则是匹配边。

其中,增广路有一个重要特点: 非匹配边比匹配边多一条 。因此,研究增广路的意义是改进匹配。只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏已经存在的匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。从这个角度分析,匈牙利算法也算作是一种贪心算法。

我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。匈牙利算法正是这么做的,这也是算法的核心。

例如,对下图中,增广路 1 → 2 → 3 → 4 → 5 → 6 1 \to 2 \to 3 \to 4 \to 5 \to 6 123456 中,可以反转匹配边和非匹配边,这样匹配边的数量就会增加一个,并且不会影响现有的匹配。

匈牙利算法

模板

主要思想:遍历左点集的每一个点,依次寻找增广路,如果存在增广路,那么就调整匹配。

寻找增广路的方式可以使用 BFS 和 DFS 寻找。

P3386

DFS

#include <bits/stdc++.h>

#define FR freopen("in.txt", "r", stdin)

using namespace std;

int n, m, t;

vector<int> graph[1005];

int match[1005];

int tag[1005];

bool dfs(int u, int t)
{

    if (tag[u] == t)
        return false; // 在一条增广路中,直接返回false

    tag[u] = t; // 给节点打上路径标记
    for (auto it = graph[u].begin(); it != graph[u].end(); it++)
    {
        int v = *it;
        if (match[v] == 0 || dfs(match[v], t)) // 如果v节点没有匹配,或者v节点的原配存在增广路(假设原配还没有匹配)
        {
            // 翻转路径
            match[v] = u;
            return true;
        }
    }

    return false;
}

int main()
{
    FR;
    cin >> n >> m >> t;

    while (t--)
    {
        int u, v;
        cin >> u >> v;
        graph[u].push_back(v);
    }

    int cnt = 0;

    for (int i = 1; i <= n; i++)
    {
        if (dfs(i,i))
        {
            // 如果存在增广路,那么调整之后匹配数必多一个
            cnt++;
        }
    }

    cout << cnt;
    return 0;
}

例题

[ZJOI2007] 矩阵游戏

我们将行号作为 U U U 列节点,将列号 V V V 列节点,那么根据初始情况,如果存在 ( x , y ) (x,y) (x,y) 中是黑色点,那么在图中连接一条 ( x , y ) (x,y) (x,y) 的边。对于行列交换来说,并不会修改图的拓扑结构,因此如果图中存在完美匹配,那么一定存在满足要求的行列交换方案。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值