標籤:flag push res 尋找 eof turn memset pre 二分圖最大匹配
本文是對二分圖大講堂這篇文章中Hopcroft-Krap演算法代碼實現的詳細注釋。
HK演算法的基本原理
Hopcroft-Karp演算法先使用BFS尋找多條增廣路,然後使用DFS遍曆增廣路(累加匹配數,修改匹配點集),迴圈執行,直到沒有增廣路為止。
Hopcroft-Karp演算法的BFS遍曆只對點進行分層(不標記是匹配點和未匹配點),然後用DFS遍曆看上面的層次哪些是增廣路徑(最後一個點是未匹配的)。
BFS過程可以看做是映像樹結構一樣逐層向下遍曆,還要防止出現相交的增廣路徑。
二分圖大講堂中給出了HK演算法步驟的通俗解釋
設U和V是圖G的二分圖,M是從U到V的匹配
(1)使用BFS遍曆對圖的點進行分層,從X中找出一個未匹配點v,(所有v)組成第一層,接下的層是這樣形成的——都是尋找匹配點(增廣路性質),直到在V中找到未匹配點才終止尋找,對X其他未匹配點同樣進行尋找增廣路徑(BFS只分層不標記是否匹配點)
(2)使用DFS遍曆尋找(1)形成的增廣路,找到就匹配數就累加1
(3)重複(1)(2)操作直到找不出增廣路徑為止
而二分圖最大匹配之Hopcroft-Karp演算法裡給了比較學術化的步驟,有興趣可以看一下
代碼註解如下
#include <bits/stdc++.h>using namespace std;const int MAXN = 1e5 + 10;const int INF = 0x3f3f3f3f;bool flag;int p, n;// 兩個集合: X和Y// Mx,My記錄結點的匹配頂點int Mx[MAXN], My[MAXN], Nx, Ny;// dx,dy是結點的BFS遍曆層次int dx[MAXN], dy[MAXN], dis;bool vst[MAXN], g[110][310];bool searchP(){ queue<int> Q; dis = INF; memset(dx, 0x3f, sizeof dx); memset(dy, 0x3f, sizeof dy); // 使用BFS遍曆對圖的點進行分層,從X中找出一個未匹配點v // (所有v)組成第一層,接下來的層都是這樣形成——每次尋找 // 匹配點(增廣路性質),直到在Y中找到未匹配點才停止尋找, // 對X其他未匹配點同樣進行尋找增廣路徑(BFS只分層不標記 // 是否匹配點) // 找出X中的所有未匹配點組成BFS的第一層 for (int i = 1; i <= Nx; ++i) { if (Mx[i] == -1) { Q.push(i); dx[i] = 0; } } while (!Q.empty()) { int u = Q.front(); Q.pop(); // 該路徑長度大於dis,等待下一次BFS擴充 // dis是到Y集合的長度,所以dis一定是一個奇數 // 而每次調用searchP只會遍曆一層X集合結點 if (dx[u] > dis) break; for (int v = 1; v <= Ny; ++v) { // (u,v)之間有邊且v還沒有分層 // 分配v的層次 if (g[u][v] && dy[v] == -1) { dy[v] = dx[u] + 1; } // v是未匹配點,停止延伸(尋找) // 得到本次BFS的最大遍曆層次 if (My[v] == -1) dis = dy[v]; // v是匹配點,繼續延伸 else { dx[My[v]] = dy[v] + 1; Q.push(My[v]); } } } // 若dis為INF說明Y中沒有未匹配點,也就是沒有增廣路徑了 return dis != INF;}// 用DFS遍曆尋找BFS形成的增廣路,如果可以找到增廣路就停止遍曆並返回truebool DFS(int u){ for (int v = 1; v <= Ny; ++v) { if (!vst[v] && g[u][v] && dy[v] == dx[u] + 1) { vst[v] = 1; // 層次(也就是增廣路徑的長度)大於本次尋找的dis // 是searchP中被break的情況,也就是還不確定是否是增廣路 // 只有等再次調用searchP再判斷 if (My[v] != -1 && dy[v] == dis) continue; // 是增廣路徑,更新匹配集 if (My[v] == -1 || DFS(My[v])) { My[v] = u; My[u] = v; return true; } } } return false;}// 尋找二分圖的最大匹配int MaxMatch(){ int res = 0; // 每個結點的匹配頂點置空 memset(Mx, 0x3f, sizeof Mx); memset(My, 0x3f, sizeof My); // 如果BFS可以找到增廣路徑 while (searchP()) { memset(vst, 0 , sizeof vst); for (int i = 1; i <= Nx; ++i) { // 用DFS尋找增廣路徑,增廣路徑一定從未匹配點開始 // 如果尋找到一個增廣路徑,匹配數加一 if (Mx[i] == -1 && DFS(i)) ++res; } } return res;}
求二分圖最大匹配——Hopcroft-Krap演算法