zoukankan      html  css  js  c++  java
  • 【Learning】带花树——一般图最大匹配

    问题

      给定一个图,求该图的最大匹配。即找到最多的边,使得每个点至多属于一条边。

      这个问题的退化版本就是二分图最大匹配。

      由于二分图中不存在奇环,偶环对最大匹配并无影响(可以调整)。所以增广路算法是可以顺利应用的。

      在一般图中,我们还是尝试使用BFS增广路的算法。

      然而一般图中还会出现奇环,在寻找增广路的时候,怎么处理奇环上的冲突?

      目的就是将奇环不断地缩起来(缩花),使得整个图在使用增广算法的时候不受影响,即不会经过奇环。

    ​  一朵由一个奇环缩点而成,一朵花里面可能还会有花。

      设这个奇环共有(2k+1)个点,那么在环内至多可以匹配到(k)条边,还会多出一个孤单的点。

      但是,形象地说,这个点可以在环里面自由移动。

      在图上将每个奇环缩成一个点成为一朵花,其实和原图是等价的,为什么?

      因为如果有合法增广路经过这朵花,在交替匹配边的时候,这朵花一定能通过那个自由点适应变化。

      画个图就明白了。

      使用并查集维护花,所有点的代表元指向这朵花里面在这次增广时BFS树中深度最浅的点。

    实现

      从每个未匹配的点开始进行BFS,找到一条合法增广路径以后,增广并退出。

      记这个未匹配的点为0类点,之后的点按10交替标序。

      每次在一个0点,枚举下一个点:

      1. 如果下一个点没有匹配,那么就找到了一个增广路,回溯并增广。

      2. 如果下一个点有匹配,那么就把它的匹配点加入队列中。

      设在搜索过程中,搜到连成环的边是((u,v))

      如果连成偶环,不需要理会;如果连成奇环,并且(u)(v)不在一朵花内,就要对整个奇环缩花了。

      搜到奇环的时候,由于每次从0点枚举下一个点,(u)(v)都是0点,环一定是这样的:

      首先要求出(u)(v)的花意义下的(lca),它也是(0)点。做法是不断暴力向上跳,实际上是两个两个地跳。

      伪代码如下:

    int getlca(int x,int y){
    	clear visit[];
        x=find(x); y=find(y);
        while(1){
        	if(x){
            	if(x has been visited) return x;
            	visit[x]=1;
          		x=find(pre[match[x]])
        	}
        	swap(x,y);
        }
    }
    

      其中(match[x])记录的是(x)的匹配点,而(pre)记录的是每个1点的BFS父亲,(find(x))返回(x)所属花的代表元。

      广义的讲,(pre[x])的定义是如果(x)点失去了当前匹配点,那么它应该匹配谁。

      然后,对整个环缩花,从((u,v))这条边向两边迭代。由于两边情况相同,一个函数调用两次即可:

    int lca=getlca(u,v);
    blossom(u,v,lca);
    blossom(v,u,lca);
    

    ​  首先是(x)(y)(pre)要互连,其次是把两个点的并查集的父亲设为(lca)(如果它是并查集的代表元,不是的话待会会遍历到的)。

      最后要将环中的1点全部扔进队列里,因为整个环缩起来了以后成为了一个点,要继续作为一个点寻找增广路,等价的做法就是把花里的所有点扔进队列(此时0点已经进过队列了所以不用扔);而缩起来的花是一个0点,故要将所有点的标号设为0(把1点设为0点就好)。

      代码中,用(s[])记录标号。

    void blossom(int x,int y,int lca){
    	while(find(x)!=lca){
    		pre[x]=y;
    		if(s[match[x]]==1){
    			s[match[x]]=0;
    			q.push(match[x]);
    		}
    		if(fa[x]==x) fa[x]=lca;
    		if(fa[match[x]]==match[x]) fa[match[x]]=lca;
    		y=match[x];
    		x=pre[y];
    	}
    }
    

    完整代码如下

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=510,M=125000;
    int n,m;
    int h[N],tot;
    int match[N],s[N],pre[N],vis[N],tim;
    int fa[N];
    queue<int> q;
    struct Edge{int v,next;}g[M*2];
    inline void addEdge(int u,int v){
    	g[++tot].v=v; g[tot].next=h[u]; h[u]=tot;
    	g[++tot].v=u; g[tot].next=h[v]; h[v]=tot;
    }
    inline int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
    int getlca(int x,int y){
    	tim++;
    	x=find(x); y=find(y);
    	for(;;x^=y^=x^=y)
    		if(x){
    			if(vis[x]==tim) return x;
    			vis[x]=tim;
    			x=find(pre[match[x]]);
    		}
    }
    void blossom(int x,int y,int lca){
    	while(find(x)!=lca){
    		pre[x]=y;
    		if(s[match[x]]==1){
    			s[match[x]]=0;
    			q.push(match[x]);
    		}
    		if(fa[x]==x) fa[x]=lca;
    		if(fa[match[x]]==match[x]) fa[match[x]]=lca;
    		y=match[x];
    		x=pre[y];
    	}
    }
    int solve(int x){
    	for(int i=1;i<=n;i++) fa[i]=i;
    	memset(s,-1,sizeof s);
    	memset(pre,0,sizeof pre);
    	while(!q.empty()) q.pop();	
    	s[x]=0;
    	q.push(x);
    	while(!q.empty()){
    		int u=q.front(); q.pop();
    		for(int i=h[u],v;i;i=g[i].next){
    			v=g[i].v;
    			if(s[v]==-1){
    				pre[v]=u; 
    				s[v]=1;
    				if(!match[v]){
    					for(int go=1;go;v=go,u=pre[go]){
    						go=match[u];
    						match[u]=v; match[v]=u;
    					}
    					return 1;
    				}
    				s[match[v]]=0;
    				q.push(match[v]);
    			}
    			else if(!s[v]&&find(u)!=find(v)){
    				int lca=getlca(u,v);
    				blossom(u,v,lca);
    				blossom(v,u,lca);
    			}
    		}
    	}
    	return 0;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	int u,v;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&u,&v);
    		addEdge(u,v);
    	}
    	int ans=0;
    	for(int i=1;i<=n;i++)
    		if(!match[i])
    			ans+=solve(i);
    	printf("%d
    ",ans);
    	for(int i=1;i<=n;i++) printf("%d ",match[i]);
    	return 0;
    }
    
  • 相关阅读:
    ngnix-内网能用,外网不能用
    python学习
    mysql数据库导出xls-自定义
    Oralce-资源配置PROFILE
    oracle-用户管理与权限分配
    Oracle-创建索引分区
    Oracle-表分区
    Oracle--索引视图序列等对象
    Oracle-数据表对象
    Oracle-管理表空间和数据文件
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/8571367.html
Copyright © 2011-2022 走看看