1. 程式人生 > 其它 >二分圖最大匹配(匈牙利演算法)

二分圖最大匹配(匈牙利演算法)

二分圖最大匹配

首先說明幾個概念:

1、一張圖 G二分圖當且僅當 G 的點集 V 可以分為兩個點集 V0,V1,滿足V0υV1=V,V0∩V1=ø。對於G的每條邊e,e的兩個端點屬於兩個不同的點集。

用自然語言來描述就是,一張圖是二分圖,當且僅當它的點可以被分成兩部分,而這張圖上的所有邊的兩個端點,都分屬不同的部分。

2、二分圖G的一個匹配是G的一個邊集E0,滿足其中的所有邊都沒有公共端點。

用自然語言來描述就是,一張圖的一個匹配是一些沒有公共端點的邊。

3、增廣路:若P是圖G中一條連通兩個未匹配頂點的路徑,並且屬於M的邊和不屬於M的邊(即已匹配和待匹配的邊)在P上交替出現,則稱P為相對於M的一條增廣路徑(舉例來說,有A、B集合,增廣路由A中一個點通向B中一個點,再由B中這個點通向A中一個點……交替進行)。

如果你仔細讀過並畫過圖,不難發現如果找到一條增廣路,那麼配對的個數就會加1。 所以說,增廣路的本質其實就是一條路徑的起點和終點都未配對的點的邊。

複雜度:

時間複雜度 : 鄰接矩陣最壞為O(n^3)
鄰接表: O(mn)
空間複雜度 : 鄰接矩陣:O(n^2)
鄰接表: O(n+m)

匈牙利演算法:

匈牙利演算法是二分圖匹配最常見的演算法,該演算法的核心就是尋找增廣路徑,它是一種用增廣路徑求二分圖最大匹配的演算法。

那下面該講下匈牙利演算法了!!!

根據上文的描述,既然增廣路的作用是“改進匹配方案”(即增加配對數),那麼如果我們已經找到了一種匹配方案,不難發現如果在當前匹配方案下再也找不到任何增廣路的話,那麼當前匹配就是二分圖的最大匹配,演算法如下。

1.首先從任意的一個未配對的點u開始,從點u的邊中任意選一條邊(假設這條邊是從u->v)開始配對。如果點v未配對,則配對成功,這是便找到了一條增廣路。如果點v已經被配對,就去嘗試“連鎖反應”,如果這時嘗試成功,就更新原來的配對關係。
所以這裡要用一個match[v] = u。配對成功就將配對數加1。

2.如果剛才所選的邊配對失敗,那就要從點u的邊中重新選一條邊重新去試。直到點u 配對成功,或嘗試過點u的所有邊為止。

3.接下來就繼續對剩下的未配對過的點一一進行配對,直到所有的點都已經嘗試完畢,找不到新的增廣路為止。

4.輸出配對數。

以上,就是匈牙利演算法的全部內容。

洛谷模板題

CODE:

 1 #include <bits/stdc++.h>
 2 #define N 1001
 3 using namespace std;
 4 int n,m,e,ans,match[N];//match用於記錄每個點所配對的點 
 5 bool a[N][N],vis[N];
 6 bool dfs(int x)
 7 {
 8     for(int i=1;i<=m;i++)
 9         if (!vis[i]&&a[x][i])//vis陣列防止往回走
10         {/*這裡有兩種情況,一種是右集中的點沒有被選,那麼它們倆構成長度為1的增廣路.
11             另一種是右集中的點已經被選了,但是往下遞迴可以發現選它的點可以有其他的選擇。
12             這樣構成了一條兩端都是非匹配點的路徑,即增廣路。*/
13             vis[i]=1;
14             if (!match[i]||dfs(match[i]))
15             {
16                 match[i]=x;//更改匹配
17                 return 1;
18             }
19         }
20     return 0;
21 }
22 int main()
23 {
24     cin>>n>>m>>e;
25     for(int i=1;i<=e;i++)
26     {
27         int u,v;cin>>u>>v;
28         if(v<=m) a[u][v]=1;//因為題目給出的資料可能會有比m大的。 
29     }
30     for(int i=1;i<=n;i++)//列舉左集中的結點
31     {
32         ans+=dfs(i);
33         memset(vis,0,sizeof(vis));//每次跑匈牙利演算法之前要清空vis陣列(重點) 
34     }
35     cout<<ans<<endl;return 0;
36 }

————THE END

by AIskeleton 2021/12/15