suxxsfe

一言(ヒトコト)

一般图最大(权)匹配

一般图最大匹配

对二分图进行匈牙利的时候,称距离交替树的根偶数条边的点是偶点,距离奇数条边的点是奇点
显然,二分图里不会有偶点和偶点的边,不会有奇点和奇点的边,如果给交替树从父亲到儿子定向,所有偶点到奇点的边是非匹配边,奇点到偶点的边是匹配边
在一般图中:

  • 对于奇点到奇点的边,是没用的,因为走不出交替路,甚至不会在搜索交替树的时候遍历他们
  • 对于偶点到偶点的边,形成的奇环上的所有奇点,都可以通过从另一边绕环行走变成偶点,这就产生了一个 “花”。称这个花上唯一没有匹配的那个点为 “花根”

感受一下花(图片来自:http://web.ntnu.edu.tw/~algo/Matching.html):

处理的思路就是既然花上的点都可以变成偶点,那么就可以把花缩点,形成一个大的偶点,这个可以用一个并查集维护。这称为一次 “缩花”
花最小有 \(3\) 个点,缩花后剩下一个点,那么缩花次数不会超过 \(\frac{n}{2}\)

于是带花树就是在匈牙利的基础上,遇到一个花就缩花
可以说明原图有增广路当且仅当缩完花的图有增广路,而且花满足一个偶点的性质,增广路起点不变,继续从新图上增广即可

\(u,v\) 是那条偶点到偶点的边,缩花的时候需要先找出花的根,可以发现这就是 \(u,v\) 在交替树上的 \(\operatorname{lca}\)
这里记录 \(match(u)\) 是目前求得的 \(u\) 的匹配(偶点奇点都记录)
\(pre(u)\)\(u\) 在交替树上的前驱,对应的是非匹配边
\(\operatorname{lca}\) 的话就暴力往上跳,\(u,v\) 轮流跳,例如 \(u\) 会跳到 \(pre(match(u))\) 在并查集上的父亲
这就跳到了上一个偶点,由于他们的 \(\operatorname{lca}\) 肯定也是偶点,所以这样一路一边跳一边打标记,肯定会跳到 \(\operatorname{lca}\),跳到有标记的点就退出就行了
这样单次复杂度是 \(O(\text{花的大小})\),但是找完 \(\operatorname{lca}\) 之后这些花上的点都会被缩成一个点,那么均摊就是 \(O(1)\)

然后就是缩花的主体,把两条路径 \(u\rightarrow \operatorname{lca},v\rightarrow \operatorname{lca}\) 分别缩起来
以第一条路径为例,还是从 \(u\) 不断往上跳,每次从 \(u\) 跳到 \(pre(match(u))\)

  • 每次看 \(match(u)\) 是不是奇点,如果是的话就改变为偶点,并入队继续增广
  • \(u,match(u)\) 都在并查集上和花根合并
  • 还要修改花上点的 \(pre\),把 \(pre(u)\) 置为上一个(就最近一次跳之前的)\(u\)\(match\),这里花上的 \(pre\) 要双向指,因为下一次便利这个些点可能从任何一个方向走进花

这样搞完一边就只有花根没有与花内的节点匹配
注意缩花的时候会经过别的已经缩过的花,虽然缩花后每个花可以当作偶点处理,但由于要更新 \(pre\),不能直接跳到 \(pre(match(u))\) 在并查集上的父亲(相当于跳到了那些缩完了的花的花根),而是跳到 \(pre(match(u))\),也就是要沿着那些花再走一边

感觉这样一来缩花过程就没有均摊 \(O(1)\) 的性质了,这样每次 bfs 缩花 \(O(n)\) 次,每次 \(O(n)\),所以总复杂度大概变成了 \(O(n^3)\)

https://uoj.ac/problem/79

#define N 1006
#define M 249506
struct Graph{
	int fir[N],nex[M],to[M],tot;
	inline void add(int u,int v,int flag=1){to[++tot]=v;nex[tot]=fir[u];fir[u]=tot;if(flag) add(v,u,0);}
	inline void clear(){__builtin_memset(fir,0,sizeof fir);tot=0;}
}G;
int n;
int dfscnt,dfn[N],fa[N];
int match[N],pre[N],color[N];
inline int find(int k){return fa[k]==k?k:fa[k]=find(fa[k]);}
inline int getLca(int u,int v){
	u=find(u);v=find(v);dfscnt++;
	while(dfn[u]^dfscnt){
		dfn[u]=dfscnt;
		u=find(pre[match[u]]);
		if(v) lib::swap(u,v);
	}
	return u;
}
int que[N],*left,*right;
inline void dealFlower(int u,int v,int root){//u,v are both even vertex
	while(find(u)^root){
		pre[u]=v;v=match[u];
		if(color[v]==2) color[v]=1,*++right=v;
		if(find(u)==u) fa[u]=root;
		if(find(v)==v) fa[v]=root;
		u=pre[v];
	}
}
inline int bfs(int u){
	__builtin_memset(color+1,0,n*sizeof color[0]);__builtin_memset(pre+1,0,n*sizeof pre[0]);
	for(int i=1;i<=n;i++) fa[i]=i;
	left=que;right=que-1;*++right=u;
	color[u]=1;pre[u]=0;
	while(left<=right){
		u=*left++;
		for(int d,v,i=G.fir[u];i;i=G.nex[i]){
			v=G.to[i];
			if(color[v]==2||find(u)==find(v)) continue;
			if(color[v]) d=getLca(u,v),dealFlower(u,v,d),dealFlower(v,u,d);//flower
			else{
				color[v]=2;color[match[v]]=1;
				if(match[v]) pre[v]=u,*++right=match[v];
				else{
					while(u) d=match[u],match[u]=v,match[v]=u,u=pre[d],v=d;
					return 1;
				}
			}
		}
	}
	return 0;
}

一般图最大权匹配

我日这都啥玩意

posted @ 2022-03-16 20:44  suxxsfe  阅读(316)  评论(0编辑  收藏  举报