• 【模板】二分图匹配/一般图匹配——匈牙利算法/随机匈牙利算法


    今天学习了匈牙利算法,顺便学习了一般图匹配的乱搞做法(被打死

    模板题传送门:
    http://uoj.ac/problem/78
    http://uoj.ac/problem/79

    二分图匹配的匈牙利算法

    简要做法

    匈牙利算法的主要思想是找增广路,和最大流算法类似。
    每次对于一个点,找出一条交替路径,即匹配边、非匹配边交错,且开头结尾都是非匹配边的路径,并将这条路径上面的边取反(取->不取,不取->取),如果增广成功,则答案+1。

    具体实现时,可以使用dfs来递归寻找增广路。

    注意点

    dfs过程中传入的顶点都是在图的一侧,同时vis数组要在每次增广之前清零,我因为这个WA了好几次...(菜不成声.jpg)

    模板代码

    //UOJ #78
    
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=505;
    void rd(int &x){
    	x=0; int ch=getchar();
    	while(ch<'0'||'9'<ch) ch=getchar();
    	while('0'<=ch&&ch<='9') x=x*10+ch-'0', ch=getchar();
    }
    int nl, nr, M, ans;
    int g[MAXN][MAXN], ma[MAXN], vis[MAXN];
    int dfs(int u){
    	for(int v=1; v<=nl; ++v){
    		if(!vis[v]&&g[u][v]){
    			vis[v]=1;
    			if(!ma[v]||dfs(ma[v])) return ma[v]=u,1;
    		}
    	}
    	return 0;
    }
    int main(){
    	rd(nl), rd(nr), rd(M);
    	for(int i=0, u, v; i<M; ++i) rd(u), rd(v), g[v][u]=1;
    	for(int i=1; i<=nr; ++i){
    		memset(vis, 0, sizeof(vis));
    		ans+=dfs(i);
    	}
    	printf("%d
    ", ans);
    	for(int i=1; i<nl; ++i) printf("%d ", ma[i]);
    	printf("%d
    ", ma[nl]);
    	return 0;
    }
    

    一般图匹配的乱搞随机做法

    正解

    对于一般图上的最大匹配问题,由于奇数长度环的存在,不能直接套用匈牙利算法。

    一个经典的解法是带花树,代码也十分简短、优美,读者可以参考网上的一些资料。

    然而博主太弱了,只会随机乱搞。

    具体做法

    外层循环T次,对于每个点,内层循环T次,寻找增广路,在每次dfs开始时,对该点的出边进行随机打乱。

    这个算法效率较高,正确率...实测也不低(如果出题人没有丧心病狂卡数据的话)。

    细节

    你可能需要一些精心选择的参数。

    顺便写了一下手写随机函数rand()。

    模板代码

    //UOJ #79
    
    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    const int MAXN=505, T=10, T2=10;
    void rd(int &x){
    	x=0; int ch=getchar();
    	while(ch<'0'||'9'<ch) ch=getchar();
    	while('0'<=ch&&ch<='9') x=x*10+ch-'0', ch=getchar();
    }
    int N, M;
    int to[MAXN][MAXN], sz[MAXN], p[MAXN];
    int m[MAXN], ma[MAXN], vis[MAXN];
    void add2(int u, int v){
    	to[u][sz[u]++]=v; to[v][sz[v]++]=u;
    }
    int rnd(){
    	static ull nxt=19260817;
    	nxt=nxt*666623333+12345;
    	return (nxt>>16)&32767;
    }
    inline void shf(int *a, int n){
    	if(!n) return;
    	for(int t=0; t<T2; ++t){
    		int x=rnd()%n, y=rnd()%n;
    		swap(a[x], a[y]);
    	}
    }
    int dfs(int u){
    	vis[u]=1; shf(to[u], sz[u]);
    	for(int i=0; i<sz[u]; ++i){
    		int v=to[u][i];
    		if(!vis[v]){
    			vis[v]=1;
    			if(!m[v]||dfs(m[v])){
    				m[v]=u; m[u]=v; return 1;
    			}
    		}
    	}
    	return 0;
    }
    int work(){
    	int ans=0;
    	for(int t1=0; t1<T; ++t1){
    		int tmp=0; memset(m, 0, sizeof(m));
    		for(int i=1; i<=N; ++i) shf(to[i], sz[i]);
    		for(int i=1; i<=N; ++i) swap(p[i], p[rnd()%i+1]);
    		for(int i=1; i<=N; ++i){
    			if(m[p[i]]) continue;
    			for(int t2=0; t2<T; ++t2){
    				memset(vis, 0, sizeof(vis));
    				if(dfs(p[i])){tmp++; break;}
    			}
    		}
    		if(tmp>ans) ans=tmp, memcpy(ma, m, sizeof(m));
    	} return ans;
    }
    int main(){
    	rd(N), rd(M);
    	for(int i=0, u, v; i<M; ++i) rd(u), rd(v), add2(u,v);
    	for(int i=1; i<=N; ++i) p[i]=i;
    	printf("%d
    ", work());
    	for(int i=1; i<N; ++i) printf("%d ", ma[i]);
    	printf("%d
    ", ma[N]);
    	return 0;
    }
    
  • 相关阅读:
    asp.net源码坊2015-3月第二周TOP10下载排行
    Asp.Net编程需要学习什么
    面试的同学看过来
    Asp.Net模板生成HTML页面
    毕业设计之房产中介系统源码
    HTML常用状态代码
    Asp.Net毕业设计论文
    网页常用Javascript
    intellij idea使用笔记
    bootstrap-table使用笔记
  • 原文地址:https://www.cnblogs.com/will7101/p/6636025.html
Copyright © 2020-2023  润新知