帶花樹演算法--一般圖最大匹配
可以在uoj#79測試模板題
如標題,簡單地介紹一下帶花樹演算法,提供一個自認為不錯的模板
安利一篇介紹得很詳細的blog
由於本人實在太蒻。。。這篇blog就不講述任何關於演算法正確性的證明吧
(也許以後會有興趣翻譯原論文:
可能有些選手會想苟蒻我一樣懶,二分圖匹配只會用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邊把需要修改的環修改掉
具體操作不妨參考本人程式碼中的函式
#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;
}