带树开花 - 带花树

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


例题:带花树例题

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

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

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

主要步骤:

首先,奇环中有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;
}
全部评论

相关推荐

头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
10-31 13:04
南华大学 Java
嵌入式的小白:很多面试,面试前不会去打扰cto的,但一般cto不会在这些小事上刷人,只能说这个cto比较操心,啥重要不重要,紧急不紧急的,估计都会过问,平淡看待吧
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务