1. 程式人生 > >超級詳細講解匈牙利演算法

超級詳細講解匈牙利演算法

二分圖匹配,江湖稱二分匹配,圖論相關演算法

現在給出兩個集合,我們拿約會來舉例子。一方是男生集合,一方是女生集合,女生都比較內斂,對不認識的男孩紙並不喜歡一起約會,所以這裡邊就要有人際關係的問題了。

這裡給男生編號n1,n2.....nn;女生編號v1v2....vn;

下面給出女生認識的男生的列表:
v1 :n1 ,n2. 

v2 :n2, n3.

v3 : n1.

這裡顯而易見,1號男生2號男生是比較受歡迎的哈~。不用演算法思想的去想這個問題我們可以這樣思考:三號女生只認識1號男生,匹配上。(1組搞定)這個時候一號女生就不能選擇1號男生了,她只能去選2號男生,這時候2號女生也就有了自己能選擇的男生,這裡我們就匹配成功了:

v1 n2 

v2 n3 

v3 n1

這裡我們就完成了匹配的過程,這個時候我們因為所有人都有了約會物件,我們這個時候稱之為最大匹配,同時也是完美匹配。

最大匹配:一個圖所有匹配中,所含匹配邊數最多的匹配,稱為這個圖的最大匹配。

完美匹配:如果一個圖的某個匹配中,所有的頂點都是匹配點,那麼它就是一個完美匹配。剛剛給出的例子就是完美匹配。

那麼我們要如何實現演算法呢?因為程式碼是不能直接看出來如何匹配能得到最大匹配的,所以我們這裡就要有一個順序去尋找最大匹配,這裡我們以編號大小的順序來尋找約會物件。

從v1開始找,先找到了n1.約上,然後是v2,找到了n2,約上。v3找到了n1,但是這裡n1和v1已經約好了,怎麼辦呢?v1對v3說:我還認識n2,我去問問他有沒有約會人選,要是沒有的話,n1讓給你。(其實我想說她是傻逼。。。。)然後v1去找n2,但是n2和v2約上了,這個時候呢v2對v1說:我還認識n3,我去看看他有沒有約會的人選,要是沒有的話n2,讓給你(這兩個傻逼。。。。)然後v2找到了n3,n3樂的屁顛屁顛的,說正好沒人約我,然後他倆約上了,v2找到了n3,那麼v1就能和v2約上了,然後v3也就能和n1約上了,我們這個時候就從剛剛的兩組匹配,轉到了3組匹配。

剛剛所述的過程,其實就是在找增廣路。(這裡增廣路的含義自己就可以理解了吧~)那麼我們如何用程式碼實現這個過程呢?其實並不難,我們這裡需要三個陣列,一個是圖,一個是詢問vis標記,一個是match匹配。

[cpp] view plain copy  print?
  1. int map[n][n];//關係圖。//這裡我們用1表示有關係0表示沒有關係
  2. int vis[n];//表示是否詢問過這個人 用1表示詢問過 0表示沒有詢問過
  3. int match[n];//陣列內資料表示匹配關係:比如match[1]=2表示男1和女2約上了(不一定最後也是在一起的)
然後就是尋找的過程: [cpp] view plain
 copy  print?
  1. int  find(int x)//這裡x表示是任何一個女生
  2. {  
  3.     for(int i=0;i<n;i++)//這裡的i表示男生
  4.     {  
  5.         if(vis[i]==0&&map[x][i]==1)//如果這個男生沒有詢問過(你總不能一直問一個人吧....本來就夠逗逼了,你還要纏著人家。。。)並且你和這個男生認識(要找到自己認識的男生啊~)
  6.         {  
  7.             vis[i]=1;//標記詢問過了
  8.             if(match[i]==-1||find(match[i]))//如果當前女孩紙是來直接找約會物件的,或者是要拋棄當前男孩去找下一個男孩的(而且找到了)。
  9.             {  
  10.                 match[i]=x;//當前男生和女生約上了(i男x女)
  11.                 return 1;//返回找到了~~~
  12.             }  
  13.         }  
  14.     }  
  15.     return 0;  
  16. }  

然後整個演算法就完美實現了。這裡對應杭電例題,希望讀者可以獨立完成:

杭電2063

過山車

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15776    Accepted Submission(s): 6918


Problem Description RPG girls今天和大家一起去遊樂場玩,終於可以坐上夢寐以求的過山車了。可是,過山車的每一排只有兩個座位,而且還有條不成文的規矩,就是每個女生必須找個個男生做partner和她同坐。但是,每個女孩都有各自的想法,舉個例子把,Rabbit只願意和XHD或PQK做partner,Grass只願意和linle或LL做partner,PrincessSnow願意和水域浪子或偽酷兒做partner。考慮到經費問題,boss劉決定只讓找到partner的人去坐過山車,其他的人,嘿嘿,就站在下面看著吧。聰明的Acmer,你可以幫忙算算最多有多少對組合可以坐上過山車嗎?
Input 輸入資料的第一行是三個整數K , M , N,分別表示可能的組合數目,女生的人數,男生的人數。0<K<=1000
1<=N 和M<=500.接下來的K行,每行有兩個數,分別表示女生Ai願意和男生Bj做partner。最後一個0結束輸入。
Output 對於每組資料,輸出一個整數,表示可以坐上過山車的最多組合數。
Sample Input 6 3 3 1 1 1 2 1 3 2 1 2 3 3 1 0
Sample Output 3 這裡貼上我的AC完整程式碼。供新接觸這個演算法的小夥伴們參考:(提醒,千萬把男生女生分清楚了,弄混了就容易WA咯~) [cpp] view plain copy  print?
  1. #include<stdio.h>
  2. #include<string.h>
  3. usingnamespace std;  
  4. constint maxn = 505;  
  5. int map[maxn][maxn];              //記錄是否可以匹配,0表示不能,1表示能
  6. int vis[maxn];                  //用在不同隊伍匹配是的標記作用
  7. int pri[maxn];  
  8. int k,m,n;  
  9. int  find(int x)  
  10. {  
  11.     for(int i=1;i<=m;i++)  
  12.     {  
  13.         if(vis[i]==0&&map[i][x])  
  14.         {  
  15.             vis[i]=1;  
  16.             if(pri[i]==-1||find(pri[i]))  
  17.             {  
  18.                 pri[i]=x;  
  19.                 return 1;  
  20.             }  
  21.         }  
  22.     }  
  23.     return 0;  
  24. }  
  25. int main()  
  26. {  
  27.     while(~scanf("%d",&k))  
  28.     {  
  29.         if(k==0)break;  
  30.         scanf ("%d%d", &m, &n);  
  31.         memset (map, 0, sizeof (map));  
  32.         memset (pri, -1, sizeof (pri));  
  33.         for(int i=0;i<k;i++)  
  34.         {  
  35.             int a,b;  
  36.             scanf("%d%d",&a,&b);  
  37.             map[a][b]=1;  
  38.         }  
  39.         int output=0;  
  40.         for(int i=1;i<=n;i++)  
  41.         {  
  42.             memset(vis,0,sizeof(vis));  
  43.             if(find(i))  
  44.             output++;  
  45.         }  
  46.         printf("%d\n",output);  
  47.     }  
  48. }