二分图匹配
记得很早就看过这个算法,但是一直没怎么学。
二分图:
无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。
判断一个联通图是否是二分图采用着色法,选取一个起点着黑色,将其相邻且未访问的点着相反色不断重复这个过程直至所有点均被着色,如果所有的边
左右都是颜色不同的点说明这是一个二分图。
显然树一定是二分图,因为他无回路。
关于二分图匹配有几个定理需要知道:
1.Berge定理
Berge匹配定理:M是图G的最大匹配<=>G中不存在M-增广路。
由定理可知,找一个图的最大匹配的关键问题就是设法寻找增广路。如果不存在所给匹配的增广路,该匹配就是最大匹配。
Berge定理为计算二分图最大匹配提供了一个思路就是不断寻找增广路直至找不到为止。
2.Hall定理
在二分图G=(X,Y;E)中,存在一个匹配M,使得X中所有点都被M匹配当且仅当对于任意S€X,都有| N(S) | >= | S | (N(S)为S的临集,V中与S通过边直接相连的点称为S的临集,记为N(S) )
利用Hall定理可以作为迭代寻找增广路的终止条件
3.
利用dfs寻找增广路的匈牙利算法: http://acm.hdu.edu.cn/showproblem.php?pid=2063
男女生分做了两个集合,问最大匹配数,每次从一个女生出发寻找增广路就好了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int match[550]; 4 bool vis[550]; 5 bool e[510][510]; 6 int K,N,M; 7 int dfs(int u){ 8 for(int i=1;i<=N;++i){ 9 if(e[u][i]&&!vis[i]){ 10 vis[i]=1; 11 if(match[i]==-1||dfs(match[i])){ 12 match[i]=u; 13 return 1; 14 } 15 } 16 } 17 return 0; 18 } 19 int main(){ 20 int i,j,k; 21 while(cin>>K&&K){ 22 cin>>M>>N; 23 memset(e,0,sizeof(e)); 24 while(K--){ 25 scanf("%d%d",&i,&j); 26 e[i][j]=1;//表示i号女生可以与j号男生配对 27 } 28 int ans=0; 29 memset(match,-1,sizeof(match)); 30 for(i=1;i<=M;++i){ 31 memset(vis,0,sizeof(vis));//清空标记,在增广路上每个点只能经过一次 32 ans+=dfs(i); 33 } 34 cout<<ans<<endl; 35 } 36 return 0; 37 }
利用konig定理:二分图最大匹配=最小点覆盖 http://acm.hdu.edu.cn/showproblem.php?pid=1054
是一个最小点覆盖的裸题,dp可解。由于是一颗树所以是二分图,又koing定理可知求出最大匹配就是答案。
由于是一颗树我们每次寻找增广路可能是从X或者Y开始的,但这不影响结果,记得先判断一下起点是否在匹配中。
1 #include<iostream> 2 #include<cstring> 3 #include<queue> 4 #include<cstdio> 5 #include<stack> 6 #include<set> 7 #include<map> 8 #include<cmath> 9 #include<ctime> 10 #include<time.h> 11 #include<algorithm> 12 using namespace std; 13 #define mp make_pair 14 #define pb push_back 15 #define debug puts("debug") 16 #define LL long long 17 #define pii pair<int,int> 18 #define eps 1e-10 19 #define inf 0x3f3f3f3f 20 21 22 vector<int>g[1510]; 23 bool used[1510]; 24 int match[1510]; 25 int N; 26 bool dfs(int u){ 27 for(int i=0;i<g[u].size();++i){ 28 int v=g[u][i]; 29 if(!used[v]){ 30 used[v]=1; 31 if(match[v]==-1||dfs(match[v])){ 32 match[u]=v; 33 match[v]=u; 34 return true; 35 } 36 } 37 } 38 return false; 39 } 40 41 int thungary(){ 42 int u,res=0; 43 memset(match,-1,sizeof(match)); 44 for(u=0;u<N;++u){ 45 memset(used,0,sizeof(used)); 46 if(match[u]==-1&&dfs(u)) res++; 47 } 48 return res; 49 } 50 int main() 51 { 52 int n,m,t,i,j,k; 53 while(scanf("%d",&n)==1){ 54 for(i=0;i<n;++i) g[i].clear(); 55 N=n; 56 int u,v; 57 for(i=1;i<=n;++i){ 58 scanf("%d:(%d)",&u,&m); 59 while(m--){ 60 scanf("%d",&v); 61 g[u].push_back(v); 62 g[v].push_back(u); 63 } 64 } 65 cout<<thungary()<<endl; 66 } 67 return 0; 68 }