[模板] 一般圖最大匹配
阿新 • • 發佈:2022-01-02
一、題目
二、解法
這裡只講演算法流程,沒有證明!沒有證明!沒有證明!
我們還是考慮沿用二分圖匹配的思路:找增廣路,我們迴圈 \(1\rightarrow n\) 找到一個沒有匹配的點,然後嘗試尋找它的匹配。我們以它為根對原圖進行 \(\tt bfs\),並且黑白交替染色,首先我們對根染黑色,然後對所有黑色點 \(u\) 訪問 \(v\):
- 如果 \(v\) 是一個沒被訪問過的點,考慮如果 \(v\) 沒有被匹配那麼我們就找到了增廣路;如果 \(v\) 被匹配那麼我們把 \(v\) 的匹配點染成黑色,並把它加入 \(\tt bfs\) 佇列中。
- 如果 \(v\) 是一個被訪問過的點,考慮如果 \(v\)
帶花樹演算法描述的是,我們此時可以把奇環縮成一個點(稱為縮花),那麼如何縮點呢?考慮使用並查集,初始時每個點的奇環頂點都是自己。縮花的時候先用暴力 \(\tt lca\) 的方法找到環的頂點,我們直接將環上所有結點的父親設定為這個頂點即可。
最後說一下實現中需要維護的重要陣列及維護方法:
- \(fa[x]\),表示 \(x\) 並查集的父親,縮花的時候維護。
- \(mat[x]\)
- \(pre[x]\),表示依據訪問順序白點 \(x\) 的上一個黑點,注意在縮花的時候需要把花上的 \(pre\) 都改成雙向的,因為花是可以雙向訪問的。
時間複雜度我也不太清楚,但是我感覺是 \(O(|V|\cdot |E|)\) 的啊?
#include <cstdio> #include <iostream> using namespace std; const int M = 1005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,tim,tot,f[M]; int d[M],pre[M],mat[M],fa[M],bz[M],bp[M]; struct edge { int v,next; }e[M*M]; int find(int x) { if(x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } int lca(int x,int y) { tim++;x=find(x);y=find(y); while(bp[x]!=tim) { bp[x]=tim; x=find(pre[mat[x]]); if(y) swap(x,y); } return x; } void make(int x,int y,int w) { while(find(x)!=w) { pre[x]=y;y=mat[x]; if(bz[y]==2) bz[y]=1,d[++d[0]]=y; if(find(x)==x) fa[x]=w; if(find(y)==y) fa[y]=w; x=pre[y]; } } int dfs(int rt) { for(int i=1;i<=n;i++) fa[i]=i,pre[i]=bz[i]=0; d[d[0]=1]=rt;bz[rt]=1;int l=0; while(l<d[0]) { int u=d[++l]; for(int i=f[u];i;i=e[i].next) { int v=e[i].v; if(find(u)==find(v) || bz[v]==2) continue; if(!bz[v]) { bz[v]=2;pre[v]=u; if(!mat[v]) { for(int x=v,y;x;x=y) y=mat[pre[x]], mat[x]=pre[x], mat[pre[x]]=x; return 1; } bz[mat[v]]=1;d[++d[0]]=mat[v]; } else { int w=lca(u,v); make(u,v,w); make(v,u,w); } } } return 0; } signed main() { n=read();m=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(); e[++tot]=edge{v,f[u]},f[u]=tot; e[++tot]=edge{u,f[v]},f[v]=tot; } int ans=0; for(int i=1;i<=n;i++) if(!mat[i]) ans+=dfs(i); printf("%d\n",ans); for(int i=1;i<=n;i++) printf("%d ",mat[i]); }