轉自: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 */
============ ========== ======== ======= ====== ===== ==== === == =
思帝鄉·春日游 唐代: 韋庄
春日游,杏花吹滿頭。陌上誰家年少,足風流?
妾擬將身嫁與,一生休。縱被無情棄,不能羞。