UOJ #79 一般圖最大匹配
阿新 • • 發佈:2018-12-24
一般圖最大匹配
從前一個和諧的班級,所有人都是搞OI的。有 \(n\) 個是男生,有 \(0\) 個是女生。男生編號分別為 \(1,…,n\)。
現在老師想把他們分成若干個兩人小組寫動態仙人掌,一個人負責搬磚另一個人負責吐槽。每個人至多屬於一個小組。
有若干個這樣的條件:第 \(v\) 個男生和第 \(u\) 個男生願意組成小組。
請問這個班級裡最多產生多少個小組?
輸入格式
第一行兩個正整數,\(n,m\)。保證 \(n≥2\)。
接下來 \(m\) 行,每行兩個整數 \(v,u\) 表示第 \(v\) 個男生和第 \(u\) 個男生願意組成小組。保證 \(1≤v,u≤n\),保證 \(v≠u\)
輸出格式
第一行一個整數,表示最多產生多少個小組。
接下來一行 \(n\) 個整數,描述一組最優方案。第 \(v\) 個整數表示 \(v\) 號男生所在小組的另一個男生的編號。如果 \(v\) 號男生沒有小組請輸出 \(0\)。
限制與約定
\(1≤n≤500\),\(1≤m≤124750\)。
是個板子,因為細節比較複雜我就不說了(其實有的自己也搞不清楚...
放個程式碼吧,有一些註釋
Code:
#include <cstdio> #include <cstring> #include <algorithm> const int N=510; int head[N],to[N*N],Next[N*N],cnt; void add(int u,int v) { to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt; } int F[N],vis[N],clock,pre[N],match[N],q[N*N],l,r,f[N],typ[N],n,m; int Find(int x){return f[x]=f[x]==x?x:Find(f[x]);} int LCA(int x,int y) { for(++clock;;std::swap(x,y)) if(x)//防止走過 { x=Find(x);//在縮掉的花朵上走 if(vis[x]==clock) return x; else vis[x]=clock,x=pre[match[x]];//一次跳兩步 } } void shrink(int x,int y,int t)//縮花 { while(Find(x)!=t)//注意是根到沒到 { pre[x]=y,y=match[x];//走x那條鏈,最開始時末尾兩個1類點互指 if(typ[y]==2) typ[y]=1,q[++r]=y;//因為不知道奇環的哪個點去匹配,所以都去試試 if(Find(x)==x) f[x]=t;//我猜加if和帶花樹的結構有關 if(Find(y)==y) f[y]=t; x=pre[y];//往前跳 } } bool path(int st) { q[l=r=1]=st; memset(typ,0,sizeof(typ)),memset(pre,0,sizeof(pre));//點的型別和非匹配邊 for(int i=1;i<=n;i++) f[i]=i; while(l<=r) { int u=q[l++]; for(int i=head[u];i;i=Next[i]) { int v=to[i]; if(typ[v]==2||Find(u)==Find(v)) continue;//已經訪問的二類點或者在同一朵花中 if(!typ[v])//不在帶花樹上 { pre[v]=u,typ[v]=2;//成為第二類點 if(!match[v]) { int tmp; do { match[tmp=v]=u; v=match[u]; match[u]=tmp; u=pre[v]; }while(u); return true; } typ[q[++r]=match[v]]=1;//成為1類點進隊 } else//形成了奇環 { int lca=LCA(u,v); shrink(u,v,lca); shrink(v,u,lca); } } } return false; } int main() { scanf("%d%d",&n,&m); for(int v,u,i=1;i<=m;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u); int ans=0; for(int i=1;i<=n;i++) ans+=(!match[i]&&path(i));//沒匹配過且存在增廣路 printf("%d\n",ans); for(int i=1;i<=n;i++) printf("%d ",match[i]); return 0; }
2018.12.24