带花树主要用来找一般图的最大匹配。


例题:带花树例题

如果是二分图,我们可以很简单的用匈牙利或者网络流等等方法找出来最大匹配。

但是如果不是二分图呢?(有奇环)

我们就需要用到带花树了。

主要步骤:

首先,奇环中有2k+1个点,所以最多有k组匹配。这就是说,有一个点没有匹配,即这个点在环内两边的连边都不是匹配边,也只有这个点可以向环外连边。

所以我们就可把整个奇环缩成一个点。缩完点后的图如果可以找到一条增广路,那么原图中也可以找到一条增广路,因为如果增广路经过奇环那么奇环内的增广路可以还原出来。

搜索到了一个未标记的点,有两种情况。如果这个未标记点有匹配,那么把这个点设为B类点,它的匹配点设为A类点,加入队列继续增广。如果这个点没有匹配,又因为我们是从一个未匹配点开始进行搜索的,所以这说明我们找到了一条增广路,沿着过来的边找回去,展开带花树,修改搜索的过程中,如果我们遇到了偶环,那么不管它,因为它不会影响求解。如果遇到了一个奇环,那么我们找到当前点x和找到的点v,求出他们的最近公共花祖先,然后把环缩掉。这里我们用并查集实现。

最后在这里,祝福chh生日快乐!!,EC拿牌!!!

总时间复杂度: O(n^3)


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=510,M=3e5+10;
int n,m,f[N],match[N],res,pre[N],col[N],cnt,tic[N];
int head[N],nex[M],to[M],tot;
queue<int> q;
inline void add(int a,int b){to[++tot]=b; nex[tot]=head[a]; head[a]=tot;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline int lca(int x,int y){
	cnt++;
	while(1){
		if(x){
			x=find(x);	
			if(tic[x]==cnt)	return x;
			else	tic[x]=cnt,x=pre[match[x]];	
		}
		swap(x,y);
	}
}
void shrink(int x,int y,int p){
	while(find(x)!=p){
		pre[x]=y,y=match[x];
		if(col[y]==2)	col[y]=1,q.push(y);
		if(find(x)==x)	f[x]=p;
		if(find(y)==y)	f[y]=p;
		x=pre[y];
	}
}
inline int check(int s){
	for(int i=1;i<=n;i++)	f[i]=i,pre[i]=col[i]=0;
	while(q.size())	q.pop();	q.push(s);	col[s]=1;
	while(q.size()){
		int u=q.front();	q.pop();
		for(int i=head[u];i;i=nex[i]){
			if(col[to[i]]==2||find(to[i])==find(u))	continue;
			if(!col[to[i]]){
				col[to[i]]=2,pre[to[i]]=u;
				if(!match[to[i]]){
					for(int j=to[i],t,last;j;j=last){
						last=match[t=pre[j]];
						match[j]=t,match[t]=j;
					}
					return 1;
				}
				col[match[to[i]]]=1;	q.push(match[to[i]]);
			}else if(col[to[i]]==1){
				int l=lca(u,to[i]);
				shrink(u,to[i],l); shrink(to[i],u,l);
			}
		}
	}
	return 0;
}
signed main(){
	cin>>n>>m;
	for(int i=1,a,b;i<=m;i++)	scanf("%d %d",&a,&b),add(a,b),add(b,a);
	for(int i=1;i<=n;i++)	res+=(!match[i]&&check(i));
	cout<<res<<endl;
	for(int i=1;i<=n;i++)	printf("%d ",match[i]);puts("");
	return 0;
}