匈牙利算法——求二部图的最大匹配的匹配数

转自:https://blog.csdn.net/dark_scope/article/details/8880547

转自:离散数学(第五版)耿素云 屈婉玲 张立昂 编著

 

一,概述

定义:若能将无向图 G = <V , E> 的顶点集 V 划分成两个不相交的非空子集 V1 和 V2,使得 G 中任何一条边的两个端点一个属于 V1,另一个属于 V2,则称 G 为 二部图。

定义:设 G = <V , E> 为无向图,M 是 E的子集,若 M 中任意两条边均不相邻,则称 M 为 G 中的 匹配。若在 M 中再加入任何一条边就都不是匹配了,则称 M 为 极大匹配,边数最多的匹配称为 最大匹配,最大匹配中边的条数称为 G 的 匹配数。显然,最大匹配是极大匹配,但反之不一定成立。

匈牙利算法:大概就是求二部图的最大匹配的匹配数,其算法核心是:寻找增广路径

 

二,算法过程描述

举例(注:本例子三观毁人,纯属为了容易理解,不包含本人观点,大家千万别较真。)

假设现有 m个男生,n个女生,每个男生可以有0到多个备胎,每个女生可以成为多个男生的备胎。如果一个女生是某个男的备胎,则可以把这一对撮合在一起,以连线表示。这种情况下,为了撮合最对多的情侣,匈牙利算法的工作模式会教你这样做:

1,总体上,你只需遍历一遍男生就可以完成该算法。

2,遍历到一号男生:尝试着给他找妹子,发现他的第一个备胎(1号女生) 还名花无主,于是两人成为情侣。

3,遍历到二号男生:尝试着给他找妹子,发现他的第一个备胎(2号女生) 还名花无主,于是两人成为情侣。

4,遍历到三号男生:尝试着给他找妹子,发现他的第一个备胎(1号女生) 已经名花有主了。说时迟那时快,三号男生直接暴走,决定和一号男生抢 1号女生的配偶权。精疲力尽的一号男生哪里是全力武装的三号男生的对手,于是乎,被抢走了1号女生。

气急败坏的一号男生决定去找他的第二个备胎(2号女生) ,结果发现2号女生 已经名花有主了。说时迟那时快,一号男生也暴走了,决定和二号男生抢 2号女生 的配偶权。精疲力尽的二号男生哪里是全力武装的一号男生的对手,于是乎,被抢走了2号女生。

气急败坏的三号男生决定去找他的第二个备胎(3号女生) ,结果发现3号女生 还名花无主,于是两人成为情侣。

(于是乎,各自都找到了女朋友,皆大欢喜(大概吧))

5,遍历到四号男生:尝试着给他找妹子,发现他的第一个备胎(3号女生) 已经名花有主了。说时迟那时快,四号男生直接暴走,决定和二号男生抢 3号女生的配偶权。精疲力尽的二号男生哪里是全力武装的四号男生的对手,于是乎,被抢走了3号女生。

气急败坏的二号男生决定去找他的下一个备胎,然而他已经没有备胎了。想到自己可能注孤生,绝望的二号男生只能返回去 ,决定和四号男生抢 3号女生的配偶权,绝望的二号男生 战胜了 大意四号男生。抱得美人归。

气急败坏的四号男生决定去找他的下一个备胎,然而他已经没有备胎了,无可奈何,孤独终老。

6,THE END

 这就是匈牙利算法的流程,其中找妹子是个递归的过程,也就是算法的核心:寻找增广路径。其意思就是 尝试霸占女生,赶跑原配。被赶跑的男生若没有备胎,则会找回来报仇,也就是说霸占失败。若被赶跑的男生之后还有备胎,则与其备胎配对,也就是说霸占成功。

 

 三,代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 505
int line[N][N];// line[i][j] 第j个女孩 是 第i个男孩 的备胎
int girl[N];  // girl[i] 表示第 i 个女孩将和 第 girl[i] 个男生 成为情侣
int vis[N];  // vis[i] 表示 是否有男孩尝试赶走原配,霸占这第 i 个女孩
int k, m, n;
int find(int x)
{
	for (int i = 1; i <= n; i++)  // 扫描每个妹子
	{
		// 如果第 i 个女孩是第 x 个男孩的备胎 ,并且这第 i 个女孩没有被别人霸占
		if (line[x][i] && !vis[i])
		{
			vis[i] = 1; // 则这第 x 个男孩尝试赶走原配,霸占这第 i 个女孩
			if (girl[i] == 0 || find(girl[i])) 
			// 则 第 x 个男孩和第 i 个女孩成为情侣 有两种情况
			// 如果 名花无主 
			// 如果 名花有主 但是这个第 girl[i] 个男孩还有其他没被霸占的备胎(find(girl[i]):即寻找增广路径)
			
			// 否则 这个第 girl[i] 个男孩 赶回来(回溯)重新抢走第 i 个女孩
			{
				girl[i] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main(void)
{
	while (scanf("%d", &k), k)
	{
		scanf("%d%d", &m, &n);
		memset(line, 0, sizeof(line));
		memset(girl, 0, sizeof(girl));

		for (int i = 0; i < k; i++)
		{
			int x, y; scanf("%d%d", &x, &y);
			line[x][y] = 1;
		}
		int sum = 0;
		for (int i = 1; i <= m; i++)
		{
			memset(vis, 0, sizeof(vis));
			if (find(i))
				sum++;
		}
		printf("%d\n", sum);
	}
	system("pause");
	return 0;
}
/*样例:
7 4 3
1 1
1 2
2 2
2 3
3 1
3 2
4 3
*/

  

 

============ ========== ======== ======= ====== ===== ==== === == = 

思帝乡·春日游  唐代: 韦庄

春日游,杏花吹满头。陌上谁家年少,足风流?
妾拟将身嫁与,一生休。纵被无情弃,不能羞。

 

posted @ 2020-11-15 15:37  叫我妖道  阅读(936)  评论(0编辑  收藏  举报
~~加载中~~