• 带花树草解


    这个东西其实看看就好, (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;
    }
    
  • 相关阅读:
    抽象类使用细节
    super关键字
    JDK,JRE,JVM三者之间的爱恨情仇
    LinkedHashSet
    HashSet扩容成红黑树机制
    Set之HashSet
    finally关键字
    Hashcode方法
    equals方法和==的区别
    LinkedList
  • 原文地址:https://www.cnblogs.com/Judge/p/10630190.html
Copyright © 2020-2023  润新知