给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
Reference:
google上搜"ByVoid 二分图"(被墙了T^T)
http://ycool.com/post/cfnym64
http://segmentfault.com/q/1010000000094642
http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html
http://note.dypanda.com/post/2012-08-07/maximal-matching-problem
计算二分图的最大匹配:匈牙利算法
模板:
#include <stdio.h> #include <string.h> #define MAX 102 long n,n1,match; long adjl[MAX][MAX]; long mat[MAX]; bool used[MAX]; FILE *fi,*fo; void readfile() { fi=fopen("flyer.in","r"); fo=fopen("flyer.out","w"); fscanf(fi,"%ld%ld",&n,&n1); long a,b; while (fscanf(fi,"%ld%ld",&a,&b)!=EOF) adjl[a][ ++adjl[a][0] ]=b; match=0; } bool crosspath(long k) { for (long i=1;i<=adjl[k][0];i++) { long j=adjl[k][i]; if (!used[j]) { used[j]=true; if (mat[j]==0 || crosspath(mat[j])) { mat[j]=k; return true; } } } return false; } void hungary() { for (long i=1;i<=n1;i++) { if (crosspath(i)) match++; memset(used,0,sizeof(used)); } } void print() { fprintf(fo,"%ld",match); fclose(fi); fclose(fo); } int main() { readfile(); hungary(); print(); return 0; }
Exercise: USACO 4.2.2 stall4 模板题
#include <stdio.h> #include <string.h> #define MAX 533 long n,n1,match,m,tm,tmp; long adjl[MAX][MAX]; long mat[MAX]; bool used[MAX]; /* void readfile() { scanf("%ld%ld",&n,&n1); long a,b; while (scanf("%ld%ld",&a,&b)!=EOF) adjl[a][ ++adjl[a][0] ]=b; match=0; } */ void readfile() { memset(adjl,0,sizeof(adjl)); scanf("%ld%ld",&n,&m); //1..n:cow n+1..n+m:stall n1=n+m; for (int i=1;i<=n;i++) { scanf("%ld",&tmp); adjl[i][0]=tmp; for (int j=1;j<=tmp;j++) { scanf("%ld",&tm); adjl[i][j]=tm+n; //adjl[tm+n][0]++; //adjl[tm+n][adjl[tm+n][0]]=i; } } match=0; } bool crosspath(long k) { for (long i=1;i<=adjl[k][0];i++) { long j=adjl[k][i]; if (!used[j]) { used[j]=true; if (mat[j]==0 || crosspath(mat[j])) { mat[j]=k; return true; } } } return false; } void hungary() { for (long i=1;i<=n1;i++) { if (crosspath(i)) match++; memset(used,0,sizeof(used)); } } int main() { freopen("stall4.in","r",stdin); freopen("stall4.out","w",stdout); readfile(); hungary(); printf("%ld ",match); return 0; }
hdu2063 模板题m个女生和n个男生,用邻接矩阵写的,这样更简单一点。
WA了好几次,竟然是因为数组没初始化233333
#include <iostream> #include <cstring> using namespace std; int a[555][555]; bool v[555]; int mat[555]; int match,n,m,k,x,y; bool crosspath(int k) { for (int j=1;j<=n;j++) { if (a[k][j]!=0) { if (!v[j]) { v[j]=true; if ((mat[j]==0)||(crosspath(mat[j]))) { mat[j]=k; return true; } } } } return false; } void hungary() { for (int i=1;i<=m;i++) { memset(v,0,sizeof(v)); if (crosspath(i)) match++; } } int main() { while (cin>>k&&k) //m:girl n:boy { cin>>m>>n; memset(a,0,sizeof(a)); memset(mat,0,sizeof(mat)); for (int i=1;i<=k;i++) { cin>>x>>y; a[x][y]=1; //a[1..m][1..n] } match=0; hungary(); cout<<match<<endl; } return 0; }
求带权最大匹配(边上有权值):KM算法
Reference:
http://yzmduncan.iteye.com/blog/912044
ByVoid 二分图带权匹配 KM算法与费用流模型建立
模板:
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/ /*最大最小有一些地方不同。。*/ const int maxn = 101; const int INF = (1<<31)-1; int w[maxn][maxn]; int lx[maxn],ly[maxn]; //顶标 int linky[maxn]; int visx[maxn],visy[maxn]; int slack[maxn]; int nx,ny; bool find(int x) { visx[x] = true; for(int y = 0; y < ny; y++) { if(visy[y]) continue; int t = lx[x] + ly[y] - w[x][y]; if(t==0) { visy[y] = true; if(linky[y]==-1 || find(linky[y])) { linky[y] = x; return true; //找到增广轨 } } else if(slack[y] > t) slack[y] = t; } return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符) } int KM() //返回最优匹配的值(这个是求最大) { int i,j; memset(linky,-1,sizeof(linky)); memset(ly,0,sizeof(ly)); for(i = 0; i < nx; i++) for(j = 0,lx[i] = -INF; j < ny; j++) if(w[i][j] > lx[i]) lx[i] = w[i][j]; //w[i][j]:distance between point#i and point#j for(int x = 0; x < nx; x++) //i:0..nx-1 j:0..ny-1 { for(i = 0; i < ny; i++) slack[i] = INF; while(true) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(find(x)) //找到增广轨,退出 break; int d = INF; for(i = 0; i < ny; i++) //没找到,对l做调整(这会增加相等子图的边),重新找 { if(!visy[i] && d > slack[i]) d = slack[i]; } for(i = 0; i < nx; i++) { if(visx[i]) lx[i] -= d; } for(i = 0; i < ny; i++) { if(visy[i]) ly[i] += d; else slack[i] -= d; } } } int result = 0; for(i = 0; i < ny; i++) result += w[linky[i]][i]; return result; }
Exercise:POJ2195
#include <iostream> #include <cstring> using namespace std; const int maxn = 110; const int INF = (1<<31)-1; int w[maxn][maxn],h[maxn][maxn],m[maxn][maxn]; int lx[maxn],ly[maxn]; int linky[maxn]; int visx[maxn],visy[maxn]; int slack[maxn]; int nx,ny,tx1,tx2,ty1,ty2,tmp,ans,n,k; char c; int abs(int x) { if (x<0) x=x*(-1); return x; } bool find(int x) { visx[x] = true; for(int y = 1; y <= ny; y++) { if(visy[y]) continue; int t = lx[x] + ly[y] - w[x][y]; if(t==0) { visy[y] = true; if(linky[y]==-1 || find(linky[y])) { linky[y] = x; return true; } } else if(slack[y] > t) slack[y] = t; } return false; } int KM() { int i,j; memset(linky,-1,sizeof(linky)); memset(ly,0,sizeof(ly)); for(i = 1; i <= nx; i++) for(j = 1,lx[i] = -INF; j <= ny; j++) if(w[i][j] > lx[i]) lx[i] = w[i][j]; for(int x = 1; x <= nx; x++) { for(i = 1; i <= ny; i++) slack[i] = INF; while(true) { memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if(find(x)) break; int d = INF; for(i = 1; i <= ny; i++) { if(!visy[i] && d > slack[i]) d = slack[i]; } for(i = 1; i <= nx; i++) { if(visx[i]) lx[i] -= d; } for(i = 1; i <= ny; i++) { if(visy[i]) ly[i] += d; else slack[i] -= d; } } } int result = 0; for(i = 1; i <= ny; i++) result += w[linky[i]][i]; return result; } int main() { while (cin>>n>>k) { if ((n==0)&&(k==0)) break; nx=0; ny=0; for (int i=1;i<=n;i++) for (int j=1;j<=k;j++) { cin>>c; if (c=='H') { nx++; h[nx][0]=i; h[nx][1]=j; } else if (c=='m') { ny++; m[ny][0]=i; m[ny][1]=j; } } for (int i=1;i<=nx;i++) for (int j=1;j<=ny;j++) { tx1=h[i][0]; ty1=h[i][1]; tx2=m[j][0]; ty2=m[j][1]; tmp=abs(tx2-tx1)+abs(ty2-ty1); w[i][j]=tmp*(-1); } ans=KM(); ans=ans*(-1); cout<<ans<<endl; } }