【模板】二分图匹配/一般图匹配——匈牙利算法/随机匈牙利算法
今天学习了匈牙利算法,顺便学习了一般图匹配的乱搞做法(被打死
模板题传送门:
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;
}
退役