可以在uoj#79測試模板題
如標題,簡單地介紹一下帶花樹演算法,提供一個自認為不錯的模板
安利一篇介紹得很詳細的blog
由於本人實在太蒻。。。這篇blog就不講述任何關於演算法正確性的證明吧
(也許以後會有興趣翻譯原論文: EfficientAlgorithmsforFindingMaximalMatchinginGraphs ,不過現在肯定是懶了。。)
可能有些選手會想苟蒻我一樣懶,二分圖匹配只會用Dinic做法,建議學習帶花樹之前看一下匈牙利演算法
就像匈牙利演算法一樣,帶花樹演算法的核心也是類似地每次找增廣路
假設對於原圖的k個點,已經找到了一些匹配,先把這些匹配留著
選擇一個尚未匹配的點s,作為起點,進行bfs,保留bfs樹
每次遍曆到一個點,如果它也沒匹配,那非常棒。因為這樣就找到了一條增廣路,直接增廣即可。
否則,記這個點為u,記它的配偶為v
對點u,標記為T節點,對於v,標記為S節點,並把v入隊,繼續bfs
(帶上起點s,顯然,每時每刻在隊列中的點都是S類節點)
那麼得到的bfs樹畫出來大概就是這樣的
其中紅色節點為起點
顯然,有的時刻,從某個點出發,路徑上下一個點是被訪問過的,哪怎麼辦。
對這個節點分類討論
1.這個點是T節點
就像圖中藍線串連的邊,顯然,此時找到了一個偶環,忽略即可
2.這個點是S節點
還是圖中的藍線,此時就找到了一個長度為奇數的環
不過這時候的情況並不簡單。。。顯然,找不到一個方案構造增廣路
但是,如果這個環上存在任意一個點,使得這個點串連的點中存在一個未匹配點
那麼顯然,此時可以構造出一條一路回溯的增廣路
那麼,這個時候這個奇環上任意一個點有邊串連未匹配點都是等價的
於是,可以將這個奇環縮成一個新點,然後再繼續進行bfs,直到找到增廣路為止(或是證明不存在)
至此,帶花樹演算法求解一般圖匹配的基本步驟就大致清晰了
bfs->縮環->bfs->縮環->…->縮環->bfs->找到增廣路
那麼找到增廣路時,就是沿路將匹配情況取反,以及,我們需要把沿途經過的環(帶花樹的花)一個個展開。。。。。。
完全沒有寫的慾望。。不知代碼從何下手。。。
這邊提供一個本人認為較容易實現的板子。。(反正這個演算法都是靠板子。)
首先,對於每次選擇的bfs起點,1~n for一遍就行了
每次判斷當前點是否已配對,如果沒有,才進行bfs
對於每個遍曆到的T類點,記錄pre[i]為找到它的S類點編號
每次出現S找到S的情況就需要進行縮環
對於縮出來的環,只找一個環上的S點作為它的代表點
如果一朵花被包含在另一朵花內,那麼這朵花就不再表示出來了
其實我們不需要真正地去做這個縮環的過程,
只需要對每朵花內的pre數組進行適當修改就行了
大概如圖所示:
這是一個S找到S的情形
只需要大力將每個S的pre邊連上就行了
顯然,如果一個點找到了合法的匹配對象(即一個未匹配點)
我們可以很輕鬆的通過pre邊把需要修改的環修改掉
具體操作不妨參考本人代碼中的函數
Link 為縮環函數
Rebuild 為上述需要更改的pre邊
Augment 則為增廣函數
#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<vector>#include<queue>#include<set>#include<map>#include<stack>#include<bitset>using namespace std;const int N = 505;int n,m,cnt,Num[105][7],fa[N],pre[N],Mark[N],mat[N],vis[N];queue <int> Q;vector <int> v[N];inline int Getfa(int x){ return x == fa[x] ? x : fa[x] = Getfa(fa[x]);}inline int getint(){ char ch = getchar(); int ret = 0; while (ch < '0' || '9' < ch) ch = getchar(); while ('0' <= ch && ch <= '9') ret = ret * 10 + ch - '0',ch = getchar(); return ret;}void Build(){ n = getint(); m = getint(); while (m--) { int x = getint(),y = getint(); v[x].push_back(y); v[y].push_back(x); }}inline void Augment(int p){ while (p != -1) { int tmp = mat[pre[p]]; mat[p] = pre[p]; mat[pre[p]] = p; p = tmp; }}inline void Rebuild(int x,int y,int lca){ while (Getfa(x) != lca) { pre[x] = y; if (fa[x] == x) fa[x] = lca; if (fa[mat[x]] == mat[x]) fa[mat[x]] = lca; if (Mark[mat[x]] == 1) {Q.push(mat[x]); Mark[mat[x]] = 0;} y = mat[x]; x = pre[y]; }}inline void Link(int x,int y){ ++cnt; int p = Getfa(x); for (;;) { vis[p] = cnt; p = mat[p]; if (p == -1) break; p = Getfa(pre[p]); } int lca; p = Getfa(y); for (;; p = Getfa(pre[mat[p]])) if (vis[p] == cnt) {lca = p; break;} Rebuild(x,y,lca); Rebuild(y,x,lca);}inline int BFS(int s){ for (int i = 1; i <= n; i++) fa[i] = i,Mark[i] = pre[i] = -1; while (!Q.empty()) Q.pop(); Mark[s] = 0; Q.push(s); while (!Q.empty()) { int k = Q.front(); Q.pop(); for (int i = 0; i < v[k].size(); i++) { int to = v[k][i]; if (Getfa(to) == Getfa(k)) continue; if (Mark[to] == -1) { Mark[to] = 1; pre[to] = k; if (mat[to] == -1) { Augment(to); return 1; } Mark[mat[to]] = 0; Q.push(mat[to]); } else if (Mark[to] == 0) Link(to,k); } } return 0;}int main(){ #ifdef DMC freopen("DMC.txt","r",stdin); #endif int Ans = 0; Build(); for (int i = 1; i <= n; i++) mat[i] = -1; for (int i = 1; i <= n; i++) if (mat[i] == -1) Ans += BFS(i); cout << Ans << endl; for (int i = 1; i <= n; i++) printf("%d%c",mat[i] == -1 ? 0 : mat[i],i == n ? '\n' : ' '); return 0;}