定義:在一個無向圖中,定義一條邊覆蓋的點為這條邊的兩個端點。找到一個邊集S包含最多的邊,使得這個邊集覆蓋到的所有頂點中的每個頂點只被一條邊覆蓋。S的大小叫做圖的最大匹配。
二分圖的最大匹配算法:設左邊集合為A集合,有邊集合為B集合。二分圖最大匹配常用的有兩種方法。
(1)第一種方法叫做匈牙利算法。這個方法依次枚舉A中的每個點,試圖在B集合中找到一個匹配。對於A集合中一點x,假設B集合中有一個與其相連的點y,若y暫時還沒有匹配點,那么x可以和y匹配,找到;否則,設y已經匹配的點為z(顯然z是A集合中的一個點),那么,我們將嘗試為z找到一個除了y之外的匹配點,若找到,那么x可以和y匹配,否則x不能與y匹配。
我們以下圖為例說明匈牙利匹配算法。
step1:從1開始,找到右側的點4,發現點4沒有被匹配,所以找到了1的匹配點為4 。得到如下圖:
step2:接下來,從2開始,試圖在右邊找到一個它的匹配點。我們枚舉5,發現5還沒有被匹配,於是找到了2的匹配點,為5.得到如下圖:
step3:接下來,我們找3的匹配點。我們枚舉了5,發現5已經有了匹配點2。此時,我們試圖找到2除了5以外的另一個匹配點,我們發現,我們可以找到7,於是2可以匹配7,所以5可以匹配給3,得到如下圖:
此時,結束,我們得到最大匹配為3。
(2)第二種方法叫做Hopcroft-Karp算法。這個算法大致思想與第一個方法相同。不同之處在於,這個方法每次找到一組互不相交的增廣路徑。我們用下面的例子說明。
step1:我們從所有未找到增廣路徑的點,也就是1,2,3,4開始,找增廣路徑,我們找到了1->2,3->3兩條(左邊的紅線表示增廣路徑),然后沿着這些增廣路徑求匹配點,得到了右邊的圖,即1匹配2,3匹配3。
一般圖的最大匹配算法:我們從一個沒有匹配的節點s開始,使用BFS生成搜索樹。每當發現一個節點u,如果u還沒有被匹配,那么就可以進行一次成功的增廣,即s匹配u;否則,我們就把節點u和它的配偶v一同接到樹上,之后把v丟進隊列繼續搜索。我們給每個在搜索樹上的點一個類型:S或者T。當u把它的配偶v扔進隊列的時候,我們把u標記為T型,v標記為S型。於是,搜索樹的樣子是這樣的:
否則,我們找到了一個長度為奇數的環,如下圖所示
就要進行一次“縮花”的操作!所謂縮花操作,就是把這個環縮成一個點。這個圖縮花之后變成了5個點(一個大點,或者叫一朵花,加原來的4個點):縮點完成之后,還要把原來環里面的T型點統統變成S型點,如下圖
無向圖最大匹配實現:
1 #define MAXN 250 2 3 class GraphMaxMatch 4 { 5 private: 6 int que[MAXN],queHead,queTail; 7 bool g[MAXN][MAXN]; 8 bool inque[MAXN],inblossom[MAXN]; 9 int match[MAXN],pre[MAXN],S[MAXN]; 10 int n; 11 12 void addQueEle(int u) 13 { 14 if(inque[u]) return; 15 inque[u]=1; 16 que[queTail++]=u; 17 if(queTail==MAXN) queTail=0; 18 } 19 int popQueEle() 20 { 21 int u=que[queHead++]; 22 if(queHead==MAXN) queHead=0; 23 return u; 24 } 25 26 int findancestor(int u,int v) 27 { 28 int visit[MAXN]; 29 memset(visit,0,sizeof(visit)); 30 while(1) 31 { 32 u=S[u]; 33 visit[u]=1; 34 if(match[u]==-1) break; 35 u=pre[match[u]]; 36 } 37 while(1) 38 { 39 v=S[v]; 40 if(visit[v]) break; 41 v=pre[match[v]]; 42 } 43 return v; 44 } 45 void reset(int u,int root) 46 { 47 int v; 48 while(u!=root) 49 { 50 v=match[u]; 51 inblossom[S[u]]=1; 52 inblossom[S[v]]=1; 53 v=pre[v]; 54 if(S[v]!=root) pre[v]=match[u]; 55 u=v; 56 } 57 } 58 59 void contract(int u,int v) 60 { 61 int root=findancestor(u,v); 62 memset(inblossom,0,sizeof(inblossom)); 63 reset(u,root); reset(v,root); 64 if(S[u]!=root) pre[u]=v; 65 if(S[v]!=root) pre[v]=u; 66 for(int i=1;i<=n;i++) if(inblossom[S[i]]) 67 { 68 S[i]=root; 69 addQueEle(i); 70 } 71 } 72 73 bool BFS(int start) 74 { 75 for(int i=1;i<=n;i++) pre[i]=-1,inque[i]=0,S[i]=i; 76 queHead=queTail=0; 77 addQueEle(start); 78 while(queHead!=queTail) 79 { 80 int u=popQueEle(); 81 82 for(int v=1;v<=n;v++) if(g[u][v]&&S[v]!=S[u]&&match[u]!=v) 83 { 84 if(v==start||match[v]!=-1&&pre[match[v]]!=-1) 85 { 86 contract(u,v); 87 } 88 else if(pre[v]==-1) 89 { 90 pre[v]=u; 91 if(match[v]!=-1) addQueEle(match[v]); 92 else 93 { 94 u=v; 95 while(u!=-1) 96 { 97 v=pre[u]; 98 int tmp=match[v]; 99 match[u]=v; 100 match[v]=u; 101 u=tmp; 102 } 103 return true; 104 } 105 } 106 } 107 } 108 return false; 109 } 110 public: 111 /** 112 vertexNum: vertex number 113 G[1~vertexNum][1~vertexNum]: edge relation 114 */ 115 vector<pair<int,int> > calMaxMatch( 116 int vertexNum,const bool G[MAXN][MAXN]) 117 { 118 n=vertexNum; 119 for(int i=1;i<=n;i++) 120 { 121 for(int j=1;j<=n;j++) g[i][j]=G[i][j]; 122 } 123 memset(match,-1,sizeof(match)); 124 for(int i=1;i<=n;i++) if(match[i]==-1) BFS(i); 125 126 vector<pair<int,int> > ans; 127 for(int i=1;i<=n;i++) 128 { 129 if(match[i]!=-1&&match[i]>i) 130 { 131 ans.push_back(make_pair(i,match[i])); 132 } 133 } 134 return ans; 135 } 136 };