带花树学习笔记

参考资料:https://rqy.moe/Algorithms/flower-tree/ https://www.cnblogs.com/owenyu/p/6858508.html

这个算法有点意思。要看更本质的理解和证明可以看 _rqy 的博客。

理论

由于有奇环的存在,匈牙利算法的理论不再适用。但是奇环也有一些性质,是可以被我们利用的。

仍然从每个点出发,尝试找一条增广路。这里我们用 BFS 来找。

在出现奇环之前, BFS 的过程可以被看做一棵 BFS 树,树上可能有返祖和横叉边。

在 BFS 的过程中,把沿途的点黑白染色。设出发点为黑色。黑点表示在原有的匹配中往上匹配,白点表示往下匹配。我们只从黑点出发找增广路。

每次从黑点 \(x\) 开始寻找增广路。普通情况比较好搞,重点考虑黑点 \(x\) 连向黑点 \(y\) 的边。

此时,我们找到了一个奇环。考虑这个奇环有什么性质:它在 BFS 树中深度最低的点,即 \(lca(x,y)\) ,一定是黑点。所以可以发现不管奇环中哪个点往外找到了一条增广路,都可以在环里面往某一个方向继续匹配。

如图, 2 往外找到了 2-6 的路径,那么可以补成 1-3-5-4-2-6 ,就成功地找到了增广路。

所以可以把环缩成一个点(\(lca(x,y)\)),环中所有点的出边都连到 \(lca(x,y)\) 上,然后继续找增广路。

可以证明新图中有增广路等价于原图中有增广路。(见 _rqy 博客)

但是朴素实现可能比较恶心,所以在实际操作中不会真的缩点。

实现

BFS ,过程中维护 \(pre\) 数组,表示找到增广路之后要往哪里跳。其另一个意义是如果 \(x\) 原有的匹配被占了,那么 \(x\) 现在应该匹配谁。

设当前点为 \(x\) ,连向的点为 \(v\)

如果 \(v\) 没有被遍历过,那么把它涂成白色。若 \(v\) 没有匹配那么说明找到了一条增广路,否则把 \(match_v\) 涂成黑色,并加入队列中。

否则,如果 \(v\) 是白色,或是已经和 \(x\) 在同一个奇环中(缩花的时候被缩在了一起),那么忽略它。

否则, \(v\) 是黑色,就说明找到了一个奇环,需要缩环。

先暴力找到 \(lca(x,v)\) ,然后分别把 \(x\to l\)\(v \to l\) 的路径缩起来。

维护 \(fa_x\) 表示所在奇环的深度最低的点是哪个。由于可能环中有环, \(fa_x\) 用并查集维护。

缩的时候把环中所有白点都涂黑并加入队列,因为此时他们往外的增广路也是有用的。

同时还要更新环中点的 \(pre\) ,每个点的 \(pre\) 为与它相邻的非匹配边的点,如上图中 \(pre_5=4,pre_4=5,pre_3=1\)

然后继续做即可。

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 555
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char __sr[1<<21],__z[20];int __C=-1,__zz=0;
    inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
    inline void print(register int x)
    {
        if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
        while(__z[++__zz]=x%10+48,x/=10);
        while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
    }
    void file()
    {
        #ifdef NTFOrz
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//	inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,m;
int e[sz][sz];

int fa[sz];
int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
int pre[sz],match[sz],tp[sz];
queue<int>q;
int vis[sz],T;
int lca(int x,int y) { for (++T;;swap(x,y)) if (x) { x=getfa(x); if (vis[x]==T) return x; vis[x]=T,x=pre[match[x]]; } }
void shrink(int x,int y,int l)
{
	while (getfa(x)!=l)
	{
		pre[x]=y,y=match[x];
		if (tp[y]==2) tp[y]=1,q.push(y);
		if (x==fa[x]) fa[x]=l;
		if (y==fa[y]) fa[y]=l;
		x=pre[y]; 
	}
}
int work(int s)
{
	while (q.size()) q.pop(); q.push(s);
	rep(i,1,n) fa[i]=i,pre[i]=0,tp[i]=0;
	tp[s]=1;
	while (!q.empty())
	{
		int x=q.front();q.pop();
		rep(v,1,n) if (e[x][v])
		{
			if (getfa(x)==getfa(v)||tp[v]==2) continue;
			if (!tp[v])
			{
				pre[v]=x;
				if (!match[v])
				{
					for (int cur=v;cur;swap(cur,match[pre[cur]])) match[cur]=pre[cur];
					match[0]=0;
					return 1;
				}
				tp[v]=2,tp[match[v]]=1,q.push(match[v]);
			}
			else { int l=lca(x,v); shrink(x,v,l),shrink(v,x,l); }
		}
	}
	return 0;
}

int main()
{
	file();
	read(n,m);
	int x,y;
	rep(i,1,m) read(x,y),e[x][y]=e[y][x]=1;
	int ans=0;
	rep(i,1,n) rep(j,i+1,n) if (!match[i]&&!match[j]&&e[i][j]) match[i]=j,match[j]=i,++ans;
	rep(i,1,n) ans+=(!match[i]&&work(i));
	printf("%d\n",ans);
	rep(i,1,n) printf("%d ",match[i]);
	return 0;
}
posted @ 2020-06-12 08:26  p_b_p_b  阅读(286)  评论(0编辑  收藏  举报