匈牙利演算法——最大匹配
問題簡介:
二分圖是指這樣的圖:圖的頂點分成X和Y兩個集合,同一個集合的頂點不存在邊相連,只有不同集合的頂點才有可能有邊直接相連。二分圖的一個匹配是指求出一個邊的集合,使得集合裡任意兩條邊都沒有公共的頂點,也就是說一個頂點最多隻屬於一條邊。
簡單地看,二分圖的一個匹配給出了一個X集合的某些頂點Y集合的某些頂點的一個一一對映。
二分圖的最大匹配(maximal matching)就是找出邊數最大的邊集,也就是求最多有多少個頂點可以被匹配上。
如果一個匹配中,圖中的每個頂點都和圖中某條邊相關聯,則稱此匹配為完全匹配,也稱作完備,完美匹配。
演算法描述:
求最大匹配的一種顯而易見的演算法是:先找出全部匹配,然後保留匹配數最多的。但是這個演算法的時間複雜度為邊數的指數級函式。因此,需要尋求一種更加高效的演算法。下面介紹用增廣路
增廣路的定義(也稱增廣軌或交錯軌):
一條增廣路的概念是指找一條已經被匹配上的邊和未被匹配上的邊相互交替的路徑,這條路徑長度為奇數並且未被匹配上的邊比被匹配上的邊多1。(因為,我要從左邊匹配到右邊)通過把增廣路上未被匹配上的邊改為匹配上的邊,而匹配上的邊全部改為未被匹配上的邊,就可以把匹配數加1。
如果這樣的增廣路不能被找到,演算法結束。
這是一種用增廣路求二分圖最大匹配的演算法。它由匈牙利數學家Edmonds於1965年提出,因而得名。 定義 未蓋點:設Vi是圖G的一個頂點,如果Vi 不與任意一條屬於匹配M的邊相關聯,就稱Vi 是一個未蓋點。
交錯路:設P是圖G的一條路,如果P的任意兩條相鄰的邊一定是一條屬於M而另一條不屬於M,就稱P是一條交錯路。
可增廣路:兩個端點都是未蓋點的交錯路叫做可增廣路。
流程圖
虛擬碼:
- bool 尋找從k出發的對應項出的可增廣路
- {
- while (從鄰接表中列舉k能關聯到頂點j)
- {
- if (j不在增廣路上)
- {
- 把j加入增廣路;
- if (j是未蓋點 或者 從j的對應項出發有可增廣路)
- {
- 修改j的對應項為k;
- 則從k的對應項出有可增廣路,返回true;
- }
- }
- }
- 則從k的對應項出沒有可增廣路,返回false;
- }
- void 匈牙利hungary()
- {
- for i->1 to n
- {
- if (則從i的對應項出有可增廣路)
- 匹配數++;
- }
- 輸出 匹配數;
- }
演示:
- #include <stdio.h>
- #include <string.h>
- #define MAX 102
- long n,n1,match;
- long adjl[MAX][MAX];
- long mat[MAX];
- bool used[MAX];
- FILE *fi,*fo;
- void readfile()
- {
- fi=fopen("flyer.in","r");
- fo=fopen("flyer.out","w");
- fscanf(fi,"%ld%ld",&n,&n1);
- long a,b;
- while (fscanf(fi,"%ld%ld",&a,&b)!=EOF)
- adjl[a][ ++adjl[a][0] ]=b;
- match=0;
- }
- bool crosspath(long k)
- {
- for (long i=1;i<=adjl[k][0];i++)
- {
- long j=adjl[k][i];
- if (!used[j])
- {
- used[j]=true;
- if (mat[j]==0 || crosspath(mat[j]))
- {
- mat[j]=k;
- returntrue;
- }
- }
- }
- returnfalse;
- }
- void hungary()
- {
- for (long i=1;i<=n1;i++)
- {
- if (crosspath(i))
- match++;
- memset(used,0,sizeof(used));
- }
- }
- void print()
- {
- fprintf(fo,"%ld",match);
- fclose(fi);
- fclose(fo);
- }
- int main()
- {
- readfile();
- hungary();
- print();
- return 0;
- }