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

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

模板题传送门:
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\n", ans);
	for(int i=1; i<nl; ++i) printf("%d ", ma[i]);
	printf("%d\n", 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\n", work());
	for(int i=1; i<N; ++i) printf("%d ", ma[i]);
	printf("%d\n", ma[N]);
	return 0;
}
posted @ 2017-03-28 17:43  will7101  阅读(237)  评论(0编辑  收藏  举报