UOJ #79 一般图最大匹配 带花树

时间:2022-10-06 10:47:49

http://uoj.ac/problem/79

一般图和二分图的区别就是有奇环,带花树是在匈牙利算法的基础上对奇环进行缩点操作,复杂度似乎是O(mn)和匈牙利一样。

具体操作是一个一个点做类似匈牙利的找增广路操作,每次将一个点作为根(染成白色),然后向下bfs黑白染色,两个白点相邻时将这两个白点缩到割顶成一个点(用并查集维护一下)(匈牙利算法也是只用白点找增广,黑点相当于重复计算了没有意义),然后把奇环里所有黑点视为白点放到队列里bfs。

设置一个pre数组记录返回的路径(因为bfs的方向和匈牙利是相反的所以最后找到的时候再顺着返回的路径重新匹配),因为重新匹配的时候找的是可匹配的黑点,所以在dfs的时候只用给每个黑点设置pre,产生奇环的时候再对白点设置pre,因为此时奇环里的点匹配之后才能决定颜色(黑白都可以)。

这个算法,有一丶丶恶心。

原理详解:https://blog.csdn.net/u014261987/article/details/41249385

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define LL long long
const int maxn=;
const int mmm=;
int n,m;
struct nod{
int y,next;
}e[maxn];
int head[mmm]={},q[maxn]={},tl=,tr=,tot=;
int fa[mmm]={},tp[mmm]={},pre[mmm]={},d[mmm]={},bel[mmm]={},tly=;
void init(int x,int y){e[++tot].y=y;e[tot].next=head[x];head[x]=tot;}
inline int getfa(int x){
return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
inline int lca(int x,int y){
++tly;
for(;;){
if(x){
x=getfa(x);
if(bel[x]==tly)return x;
else {bel[x]=tly;x=pre[d[x]];}
}
swap(x,y);
}
}
inline void mlink(int x,int y,int pa){
while(getfa(x)!=pa){
pre[x]=y;y=d[x];
if(tp[y]==){tp[y]=;q[++tr]=y;}
if(getfa(x)==x)fa[x]=pa;
if(getfa(y)==y)fa[y]=pa;
x=pre[y];
}
}
int doit(int s){
for(int i=;i<=n;++i)fa[i]=i;
memset(tp,,sizeof(tp));memset(pre,,sizeof(pre));
tl=tr=;q[]=s;tp[s]=;
while(tl<=tr){
int x=q[tl];++tl;
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
if(getfa(x)==getfa(y)||tp[y]==)continue;
if(!tp[y]){
tp[y]=;pre[y]=x;
if(!d[y]){
for(int now=y,las,j;now;now=las){
j=pre[now]; las=d[pre[now]];
d[now]=j;d[j]=now;
}
return ;
}
tp[d[y]]=;q[++tr]=d[y];
}
else{
int pa=lca(x,y);
mlink(x,y,pa);mlink(y,x,pa);
}
}
}return ;
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
for(int i=;i<m;++i){scanf("%d%d",&x,&y);init(x,y);init(y,x);}
int ans=;
for(int i=;i<n;++i){if(!d[i])ans+=doit(i);}
printf("%d\n",ans);
for(int i=;i<=n;++i)if(d[i])d[d[i]]=i;
for(int i=;i<=n;++i){
printf("%d ",d[i]);
}printf("\n");
return ;
}