1、男生女生一起坐过山车,每一排有两个座位,但是有个条件,就是每个女生必须找个男生做同伴一起(但是女生只愿意和某几个男生中的一个做同伴),求最多可以有多少对男女生组合坐上过山车。
2、二分图的最大匹配,女生作为X集合(左边),男生作为Y集合(右边)
3、
3.1匈牙利算法(邻接矩阵):
/* 顶点编号从0开始的 邻接矩阵(匈牙利算法) 二分图匹配(匈牙利算法的DFS实现)(邻接矩阵形式) 初始化:g[][]两边顶点的划分情况 建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配 g没有边相连则初始化为0 uN是匹配左边的顶点数,vN是匹配右边的顶点数 左边是X集,右边是Y集 调用:res=hungary();输出最大匹配数 优点:适用于稠密图,DFS找增广路,实现简洁易于理解 时间复杂度:O(VE) */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAXN=512; int uN,vN;//u,v 的数目,使用前面必须赋值 int g[MAXN][MAXN];//邻接矩阵,记得初始化 int linker[MAXN];//linker[v]=u,表示v(右边Y集合中的点)连接到u(左边X集合中的点) bool used[MAXN]; bool dfs(int u){//判断以X集合中的节点u为起点的增广路径是否存在 for(int v=0;v<vN;v++)//枚举右边Y集合中的点 if(g[u][v]&&!used[v]){//搜索Y集合中所有与u相连的未访问点v used[v]=true;//访问节点v if(linker[v]==-1||dfs(linker[v])){//是否存在增广路径 //若v是未盖点(linker[v]==-1表示没有与v相连的点,即v是未盖点),找到增广路径 //或者存在从与v相连的匹配点linker[v]出发的增广路径 linker[v]=u;//设定(u,v)为匹配边,v连接到u return true;//返回找到增广路径 } } return false; } int hungary(){//返回最大匹配数(即最多的匹配边的条数) int res=0;//最大匹配数 memset(linker,-1,sizeof(linker));//匹配边集初始化为空 for(int u=0;u<uN;u++){//找X集合中的点的增广路 memset(used,false,sizeof(used));//设Y集合中的所有节点的未访问标志 if(dfs(u))res++;//找到增广路,匹配数(即匹配边的条数)+1 } return res; } int main(){ int i,ans; int K,M,N;//K边数,M是左边的顶点数,N是右边的顶点数 int u,v; while(~scanf("%d",&K)){ if(K==0)break; scanf("%d%d",&M,&N); uN=M;//匹配左边的顶点数 vN=N;//匹配右边的顶点数 memset(g,0,sizeof(g));//二分图的邻接矩阵初始化 for(i=0;i<K;++i){ scanf("%d%d",&u,&v); g[--u][--v]=1;//顶点编号从0开始的 } ans=hungary(); printf("%d ",ans); } return 0; }
3.2匈牙利算法(邻接表):
/* //顶点编号从0开始的 匈牙利算法邻接表形式 使用前用init()进行初始化,给uN赋值 加边使用函数addedge(u,v) */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAXN=512;//点数的最大值 const int MAXM=1024;//边数的最大值 struct Edge{ int to,next; }edge[MAXM]; int head[MAXN],tot; void init(){ tot=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } int linker[MAXN]; bool used[MAXN]; int uN; bool dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!used[v]){ used[v]=true; if(linker[v]==-1||dfs(linker[v])){ linker[v]=u; return true; } } } return false; } int hungary(){ int res=0; memset(linker,-1,sizeof(linker)); //点的编号0~uN-1 for(int u=0;u<uN;u++){ memset(used,false,sizeof(used)); if(dfs(u))res++; } return res; } int main(){ int i,ans; int K,M,N; int u,v; while(~scanf("%d",&K)){ if(K==0)break; scanf("%d%d",&M,&N); uN=M;//匹配左边的顶点数 init();//初始化 for(i=0;i<K;++i){ scanf("%d%d",&u,&v); addedge(--u,--v);//顶点编号从0开始的 } ans=hungary(); printf("%d ",ans); } return 0; }
3.3匈牙利算法(邻接表,用vector实现):
/* //顶点编号从1开始的 用STL中的vector建立邻接表实现匈牙利算法 效率比较高 处理点比较多的效率很高。1500的点都没有问题 */ #include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> using namespace std; const int MAXN=512;//这个值要超过两边个数的较大者,因为有linker int linker[MAXN]; bool used[MAXN]; vector<int>G[MAXN]; int uN; bool dfs(int u) { int sz=G[u].size(); for(int i=0; i<sz; i++) { if(!used[G[u][i]]) { used[G[u][i]]=true; if(linker[G[u][i]]==-1||dfs(linker[G[u][i]])) { linker[G[u][i]]=u; return true; } } } return false; } int hungary() { int u; int res=0; memset(linker,-1,sizeof(linker)); for(u=1; u<=uN; u++) { memset(used,false,sizeof(used)); if(dfs(u)) res++; } return res; } int main() { int i,ans; int K,M,N; int u,v; while(~scanf("%d",&K)) { if(K==0)break; scanf("%d%d",&M,&N); uN=M;//匹配左边的顶点数 for(i=0; i<MAXN; i++)//二分图的邻接表初始化 G[i].clear(); for(i=0; i<K; ++i) { scanf("%d%d",&u,&v); G[u].push_back(v);//顶点编号从1开始的 } ans=hungary(); printf("%d ",ans); } return 0; }
3.4Hopcroft-Karp算法(邻接矩阵):
/* ********************************************* //顶点编号从1开始的 二分图匹配(Hopcroft-Carp的算法)。 初始化:g[][]邻接矩阵 调用:res=MaxMatch(); Nx,Ny要初始化!!! 时间复杂大为 O(V^0.5 E) 适用于数据较大的二分匹配 需要queue头文件 ********************************************** */ #include<iostream> #include<stdio.h> #include<queue> #include<string.h> using namespace std; const int MAXN=512; const int INF=1<<28; int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny; int dx[MAXN],dy[MAXN],dis; bool vst[MAXN]; bool searchP() { queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=1; i<=Nx; i++) if(Mx[i]==-1) { Q.push(i); dx[i]=0; } while(!Q.empty()) { int u=Q.front(); Q.pop(); if(dx[u]>dis) break; for(int v=1; v<=Ny; v++) if(g[u][v]&&dy[v]==-1) { dy[v]=dx[u]+1; if(My[v]==-1) dis=dy[v]; else { dx[My[v]]=dy[v]+1; Q.push(My[v]); } } } return dis!=INF; } bool DFS(int u) { for(int v=1; v<=Ny; v++) if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1) { vst[v]=1; if(My[v]!=-1&&dy[v]==dis) continue; if(My[v]==-1||DFS(My[v])) { My[v]=u; Mx[u]=v; return 1; } } return 0; } int MaxMatch() { int res=0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(searchP()) { memset(vst,0,sizeof(vst)); for(int i=1; i<=Nx; i++) if(Mx[i]==-1&&DFS(i)) res++; } return res; } int main() { int i,ans; int K,M,N; int u,v; while(~scanf("%d",&K)) { if(K==0)break; scanf("%d%d",&M,&N); Nx=M;//匹配左边的顶点数 Ny=N;//匹配右边的顶点数 memset(g,0,sizeof(g));//二分图的邻接矩阵初始化 for(i=0; i<K; ++i) { scanf("%d%d",&u,&v); g[u][v]=1;//顶点编号从1开始的 } ans=MaxMatch(); printf("%d ",ans); } return 0; }
3.5Hopcroft-Karp算法(邻接表,用vector实现):
/* //顶点编号从0开始的 二分图匹配(Hopcroft-Karp算法) 复杂度O(squt(n)*E) 邻接表存图,vector实现 vector先初始化,然后加入边 uN为左端的顶点数,使用前赋值(点编号0开始) */ #include<iostream> #include<stdio.h> #include<vector> #include<queue> #include<string.h> using namespace std; const int MAXN=512; const int INF=0x3f3f3f3f; vector<int>G[MAXN]; int uN; int Mx[MAXN],My[MAXN]; int dx[MAXN],dy[MAXN]; int dis; bool used[MAXN]; bool SearchP(){ queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<uN;i++) if(Mx[i]==-1){ Q.push(i); dx[i]=0; } while(!Q.empty()){ int u=Q.front(); Q.pop(); if(dx[u]>dis)break; int sz=G[u].size(); for(int i=0;i<sz;i++){ int v=G[u][i]; if(dy[v]==-1){ dy[v]=dx[u]+1; if(My[v]==-1)dis=dy[v]; else{ dx[My[v]]=dy[v]+1; Q.push(My[v]); } } } } return dis!=INF; } bool DFS(int u){ int sz=G[u].size(); for(int i=0;i<sz;i++){ int v=G[u][i]; if(!used[v]&&dy[v]==dx[u]+1){ used[v]=true; if(My[v]!=-1&&dy[v]==dis)continue; if(My[v]==-1||DFS(My[v])){ My[v]=u; Mx[u]=v; return true; } } } return false; } int MaxMatch(){ int res=0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(SearchP()){ memset(used,false,sizeof(used)); for(int i=0;i<uN;i++) if(Mx[i]==-1&&DFS(i)) res++; } return res; } int main(){ int i,ans; int K,M,N; int u,v; while(~scanf("%d",&K)){ if(K==0)break; scanf("%d%d",&M,&N); uN=M;//匹配左边的顶点数 for(i=0;i<MAXN;++i)//二分图的邻接表初始化 G[i].clear(); for(i=0;i<K;++i){ scanf("%d%d",&u,&v); G[--u].push_back(--v);//顶点编号从0开始的 } ans=MaxMatch(); printf("%d ",ans); } return 0; }