二分图匹配之匈牙利算法(超级详细,看完不懂都难)

最近刚学了二分图匹配,发篇博客分享一下。


要了解二分图匹配首先要知道二分图是什么:

  “设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。” 

  摘自百度百科

用人话来说就是把一个图中的点分成两个集合,然后一个集合中的点只能和另一个集合中的点连边,这样的一个图就是二分图。

不要嫌烦,我们再来看看一些相关名词:

匹配:再图中选择若干个边,任意两个边没有公共顶点。

最大匹配:最大的匹配方案。

那么什么是二分图匹配呢?顾名思义就是在一个二分图中寻找他的最大匹配。


知道了二分图匹配的定义,我们接下来用一个生(wu)动(liao)的例子来理解二分图匹配的应用以及算法流程:

在遥远的未来,拥有世界上大部分财富的人同时拥有了这世界上大部分的女人,这导致大部分正常男人根本就没有伴侣。

政府为了解决这个问题,取消了原有的婚姻制度,取而代之的是一个强制性的婚姻制度。在这个婚姻制度中,一旦年龄超过18岁,公民便会被强制匹配一个伴侣,必须与其共度余生。而政府为了公民的婚姻更加和谐,会事先评估每个人的性格,然后尽可能将性格合适的匹配在一块。然而不一定能让所以人都找到性格合适的伴侣,这时就需要“逼婚员”的努力,让大家的婚姻尽可能的和谐。

逼婚员今天的任务可以用下面这张丑陋的图表示:

(方形点表示男生,椭圆点表示女生,边表示他们性格合适)

逼婚员都收过专业的训练,他们匹配都有特殊的方法。

让我们看看逼婚员怎么处理今天的任务吧。首先他把男生A和女生A匹配在了一块。

然后同时又看到男生B和女生A也匹配,而且男生A除了和女生A匹配,还和女生B匹配,于是就把女生B匹配给了男生A,女生A匹配给男生B。

 剩下三个直接匹配就完事了。。。


 通过上面的例子可以看到匈牙利算法的思想非常暴力,就是对于个边,能连就直接连,不能连就尝试让之前的点给当前点腾出来一个点。

现在我们来看看代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n, m, e, ans = 0,
link[N]; bool vis[N];//link[v]表示v连向的点, vis表示某个点是否被访问过。
vector<int> g[4*N];//vector存图
//快读
inline int read() {
       register int x = 0, t = 1; register char ch = getchar();
       while ((ch < '0' || ch > '9') && ch != '-')ch = getchar();
       if (ch=='-') { t = -1; ch = getchar(); }
       while (ch >= '0' && ch <= '9'){ x = (x<<3) + (x<<1) + (ch^48); ch = getchar(); }
       return x*t;
}
//算法核心
bool dfs(int x) {
	for (int i = 0; i < g[x].size(); i++) {
		int v = g[x][i];
                //如果没被访问
		if (!vis[v]) {
			vis[v] = 1;
			if (link[v] == -1 || dfs(link[v])) { //若是v还没有被配对,就把v配对给x,否则让link[v]腾出v给它。
				link[v] = x; //把v连接到x
				return 1; //表示x能配对到点
			}
		}
	} return 0; //x不能配对到点
}

int main() {
	memset(link, -1, sizeof(link));
	n = read(), m = read(), e = read();
	for (int i = 1; i <= e; i++) {
		int u = read(), v = read();
		if (u > n || v > m || u < 1 || v < 1) continue;
		g[u].push_back(v+n); //建边,注意一定要是单向边
	} for (int i = 1; i <= n; i++) {
		memset(vis, 0, sizeof(vis));
		if (dfs(i)) ans++; //如果能匹配到答案加一
	} cout << ans;
	return 0;
}           

  

猜你喜欢

转载自www.cnblogs.com/memory2018/p/10563861.html