匈牙利算法——求二部圖的最大匹配的匹配數


轉自: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
*/

  

 

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

思帝鄉·春日游  唐代: 韋庄

春日游,杏花吹滿頭。陌上誰家年少,足風流?
妾擬將身嫁與,一生休。縱被無情棄,不能羞。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM