UOJ #79 一般图最大匹配
一般图最大匹配
从前一个和谐的班级,所有人都是搞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