匈牙利算法(二分圖最大匹配)算法
考慮一下二分圖和通常圖的最大區別(或者說惟一的區別在哪裏)。
二分圖沒有奇環(也就是長度爲奇數的環),而通常圖是能夠有的。
因此匈牙利算法中的尋找增廣路而後路徑取反的方法在通常圖上就不適用了。數組
主要仍是要解決奇環的問題。
咱們發現一個奇環裏至少有一個點不能匹配,那就乾脆把一個奇環縮成一個點(開花)?
在處理到奇環的時候把它縮成一個點,路徑取反的時候再暴力展開一個個取反。spa
咱們給全部點黑白染色。假設開始增廣的點是黑點。
把全部黑點壓進隊列中順次處理。對於一個黑點\(u\),找與他相鄰的點\(v\),會出現一下幾種狀況:code
一、\(u,v\)已經被縮成一個點了(這兩個點在一朵花裏),無論他。blog
二、\(v\)是白點,說明已經被匹配了,也無論。隊列
三、\(v\)尚未被染色。那就先把這個點染成白的,而後嘗試去與他匹配。若是\(v\)尚未匹配就匹配上,增廣成功,而後一路跳回取反。若是\(v\)已經被匹配了,那麼匹配他的點就是個黑點,染色,而後壓進隊列。get
四、\(v\)也是黑點。這時候染色發生了衝突,說明碰見了奇環。這時候就須要找到兩個點的\(lca\),而後把這整個環縮成一個點。美其名曰,開花。string
開花的時候大體要作這麼幾件事:(摘自無向圖匹配的帶花樹算法)
1。找\(x\)和\(y\)的\(LCA\)(的根)\(p\)。找\(LCA\)能夠用各類方法。。。直接樸素也行。
2。在\(pre\)數組中把\(x\)和\(y\)接起來(表示它們造成環了!)
3。從\(x\)、\(y\)分別走到\(p\),修改並查集使得它們都變成一家人,同時沿路把\(pre\)數組接起來。it
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; int gi() { int x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-') w=0,ch=getchar(); while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return w?x:-x; } const int N = 505; int n,m,to[N*N<<1],nxt[N*N<<1],head[N],cnt; int match[N],pre[N],vis[N],fa[N],tim[N],idx,ans; queue<int>Q; int link(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;} int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int lca(int x,int y) { for (++idx;;swap(x,y)) if (x) { x=find(x); if (tim[x]==idx) return x; else tim[x]=idx,x=pre[match[x]]; } } void blossom(int x,int y,int p) { while (find(x)!=p) { pre[x]=y;y=match[x]; if (vis[y]==2) vis[y]=1,Q.push(y); if (find(x)==x) fa[x]=p; if (find(y)==y) fa[y]=p; x=pre[y]; } } int Aug(int S) { for (int i=1;i<=n;++i) vis[i]=pre[i]=0,fa[i]=i; while (!Q.empty()) Q.pop(); Q.push(S);vis[S]=1; while (!Q.empty()) { int u=Q.front();Q.pop(); for (int e=head[u];e;e=nxt[e]) { int v=to[e]; if (find(u)==find(v)||vis[v]==2) continue; if (!vis[v]) { vis[v]=2;pre[v]=u; if (!match[v]) { for (int x=v,lst;x;x=lst) lst=match[pre[x]],match[x]=pre[x],match[pre[x]]=x; return 1; } vis[match[v]]=1,Q.push(match[v]); } else { int gg=lca(u,v); blossom(u,v,gg);blossom(v,u,gg); } } } return 0; } int main() { n=gi();m=gi(); for (int i=1;i<=m;++i) { int u=gi(),v=gi(); link(u,v);link(v,u); } for (int i=1;i<=n;++i) if (!match[i]) ans+=Aug(i); printf("%d\n",ans); for (int i=1;i<=n;++i) printf("%d ",match[i]); puts("");return 0; }