正题
这节的东西理解起来可能有点难,但是相信这篇博客能够帮到你们。
首先,我们要发现一般图和二分图的区别。
很容易就可以知道,当增广路不存在环的时候,和二分图是没有任何差异的。
我们可以直接用一个没有匹配的点去找点,如果找到的点匹配过,我们就把他们的匹配点放入队列,然后知道找到一个未匹配点,再从头到尾更新增广路。
但是假如有环呢?
我们又可以发现,当出现偶环的时候,上面的寻找过程还是成立的。
出现奇环了呢?发现如果更新增光路,这个奇环的一个点会连着两条匹配边。
这就不满足题设了。
怎么办?
带花树算法正是解决奇环的妙招。
在算法中,我们把奇环就叫做花。
我们从一个没有匹配的begin点开始找,并把它标为1类点(tp[begin]=1),找到一个y,假如y没有被匹配过,那么可以直接还原增广路,倒着处理匹配,是的新匹配比原匹配多1.
假如y没有被找过了。我们把它标为2类点。并把他的匹配点标为1类点,加入队列。
假如y被找过了,那就要分两种情况讨论。
1.它是2类点,说明形成了偶环(因为1类点才会放入队列,2类点没有“找”的权利),这时,我们可以不用理这个偶环,因为他不会对题设产生影响(也就是说不会存在增广之后存在一个点两条匹配边)。
2.它是1类点, 说明形成了奇环,这时这个环就可能对题设产生影响,我们要规定一些东西,才能使它满足题设。
怎么规定呢?
在平常的增广路算法中,就是找到路就增广,但在修改匹配的时候,如果经过了一个奇环,可能被夺走的点的匹配点是没有一个前驱的,就是说,在遍历的时候是直接遍历到被夺走的点,变得无路可走。
我们让两个有边的匹配,头尾互相为前一个即可。
也就是说,我们使增广路不走奇环。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int n,m;
struct edge{
int y,next;
}s[250010];
int first[510],len=0,match[510],fa[510],tp[510],pre[510],tf[510],tim=0;
queue<int> f;
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
int findpa(int x){
if(fa[x]!=x) return fa[x]=findpa(fa[x]);
return x;
}
int lca(int x,int y){
for(tim++;;swap(x,y))
if(x){
x=findpa(x);
if(tf[x]==tim) return x;
tf[x]=tim;x=pre[match[x]];
}
}
void make(int x,int y,int t){
while(findpa(x)!=t){
pre[x]=y;y=match[x];
if(tp[y]==2) tp[y]=1,f.push(y);
fa[x]=t;fa[y]=t;
x=pre[y];
}
}
bool Aug(int begin){
for(int i=1;i<=n;i++) fa[i]=i;
memset(tp,0,sizeof(tp));memset(pre,0,sizeof(pre));
while(!f.empty()) f.pop();
tp[begin]=1;
f.push(begin);
while(!f.empty()){
int x=f.front();f.pop();
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(findpa(x)==findpa(y) || tp[y]==2) continue;
if(!tp[y]){
tp[y]=2;pre[y]=x;
if(!match[y]){
for(int last;x;y=last,x=pre[last]) last=match[x],match[x]=y,match[y]=x;
return true;
}
tp[match[y]]=1;f.push(match[y]);
}
else{
int op=lca(x,y);
make(x,y,op);make(y,x,op);
}
}
}
return false;
}
int main(){
scanf("%d %d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
ins(x,y);ins(y,x);
}
int ans=0;
for(int i=1;i<=n;i++) ans+=(!match[i] && Aug(i));
printf("%d\n",ans);
for(int i=1;i<=n;i++) printf("%d ",match[i]);
}