• 带花树草解


    这个东西其实看看就好, (n^3) 次的做法虽说也是多项式复杂度但总给人很暴力的感觉...

    链接:uoj #79

    我们都知道二分图最大匹配可以匈牙利、网络流,因为二分图只有一边连向另一边,换句话说就是不存在奇环(可以简单证明),这是增广路算法可以解决的

    对于有向无环图,网络流算法也可以跑得非常优秀,但是对于一般图来说,这两个算法都束手无策了

    一般图的背景一般是一堆男生(n 个), 0 个女生,两个男生间存在边,表示某种关♂系,然后求最大匹配数

    那么这样的图中是有可能存在奇环的,奇环没有增广路(本来都不知道增广路是个啥,写这篇随笔的时候才去看...),

    这时候就要树上开花啦~

    我们考虑和匈牙利时一样,每次帮一个点匹配,但是这里用的是广搜的方式(匈牙利是深搜),建出 bfs 树并在上面进行相应的奇环缩点操作(也就是拿出一个点,剩下的两两匹配),因为可以证明奇环上任意一个点被匹配后其他点都可以两两匹配

    这里的 match 如同名称表示与谁匹配,并且这里可以将 pre 数组理解为备胎匹配的数组

    code

    //by Judge
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define Rg register
    #define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
    #define go(G,u) for(Rg int i=G.head[u],v=G.e[i].to;i;v=G.e[i=G.e[i].nxt].to)
    #define ll long long
    using namespace std;
    const int inf=1e9+7;
    const int N=503;
    const int M=N*N;
    typedef int arr[N];
    #ifndef Judge
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    #endif
    char buf[1<<21],*p1=buf,*p2=buf;
    inline int read(){ int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    } char sr[1<<21],z[20];int CCF=-1,Z;
    inline void Ot(){fwrite(sr,1,CCF+1,stdout),CCF=-1;}
    inline void print(int x,char chr=' '){
        if(CCF>1<<20)Ot();if(x<0)sr[++CCF]=45,x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++CCF]=z[Z],--Z);sr[++CCF]=chr;
    } int n,m,tim,ans,h,t,q[M]; arr f,vis,pre,match,ty;
    struct Gr{ int pat,head[M];
    	struct Edge{int to,nxt;}e[M<<1];
    	inline void add(int u,int v){
    		e[++pat]={v,head[u]},head[u]=pat;
    		e[++pat]={u,head[v]},head[v]=pat;
    	}
    }G;
    int find(int x){return f[x]^x?f[x]=find(f[x]):x;}
    inline int LCA(int x,int y){ //两个点轮流跳 fa,直到一个访问过的另一个访问到了 
    	for(++tim;;swap(x,y)) if(x){ x=find(x);
    		if(vis[x]==tim) return x;
    		vis[x]=tim,x=pre[match[x]];
    	}
    }
    inline void shrink(int x,int y,int k){
    	while(find(x)^k){
    		pre[x]=y,y=match[x]; // 匹配的两个点的 fa 都设为 lca 
    		if(ty[y]==2) ty[y]=1,q[++t]=y; //其余点的 type 都变为 1 ,表示是 S 类点 
    		if(find(x)==x) f[x]=k;
    		if(find(y)==y) f[y]=k;
    		x=pre[y];
    	}
    }
    bool path(int S){ q[h=t=1]=S; //去给 S 找匹配(类似匈牙利拆东墙补西墙)
    	fp(i,1,n) ty[i]=pre[i]=0,f[i]=i; //清空 pre 
    	for(int u;h<=t;++h){ u=q[h];
    		go(G,u) if(find(u)^find(v)&&ty[v]^2) //除去缩成一个点的情况以及已经是 T 类点 
    			if(!ty[v]){ //没有访问过的情况 
    				pre[v]=u,ty[v]=2;
    				if(!match[v]){ //当前点没有被匹配的话目的就已经达到了,直接修改树上一条链的匹配关系 
    					int tmp;
    					do{ tmp=v,
    						match[v]=u,v=match[u],
    						match[u]=tmp,u=pre[v];
    					}while(u); //修改链上的匹配关系 
    					return 1;
    				} q[++t]=match[v],ty[match[v]]=1; //当前点匹配了,就连同匹配点一起丢到队列中  
    			} else{ //形成奇环,开始缩点 
    				int lca=LCA(u,v); //找到 bfs 树上的 lca 
    				shrink(u,v,lca),shrink(v,u,lca); //缩点 
    			}
    	} return 0;
    }
    int main(){ n=read(),m=read(); int x,y; //主函数都比较朴素 
    	fp(i,1,m) x=read(),y=read(),G.add(x,y);
    	fp(i,1,n) ans+=(!match[i]&&path(i));
    	print(ans,'
    '); fp(i,1,n) print(match[i]);
    	return Ot(),0;
    }
    
  • 相关阅读:
    关于在MAC上进行 LARAVEL 环境 Homestead 安装过程记录
    js 贷款计算器
    js 实现阶乘
    js 两点间距离函数
    composer Your requirements could not be resolved to an installable set of packages
    vue 项目优化记录 持续更新...
    vue 项目打包
    vue 真机调试页面出现空白
    vue 真机调试
    谈谈-Android状态栏的编辑
  • 原文地址:https://www.cnblogs.com/Judge/p/10630190.html
Copyright © 2020-2023  润新知