P2756 飞行员配对方案问题
二分图
稍微讲一下二分图:
就是一个图分成两个部分A和B,这个图的无向边只连接不同的两个部分,即边不能在A或B的内部自己连自己
然后问你最大能找到多少匹配
匹配是指 A中的一个点与B中的一个点有边 并且 这两个点都还没有与其它点匹配
这种问题一般用匈牙利算法:
匈牙利算法的思想很简单
对于一个点a能否找到另一个点匹配
深搜a
如果与a相连的这个点b还没匹配
那么毫无问题a可以和b匹配
但是如果b有匹配了
没事,去问一下与b匹配的点能不能找其它的点匹配
方法就是继续深搜(深搜与b匹配的点能否找到再另一个点匹配......)
然后一直下去
如果最终找到了
回来,更新匹配
本题是十分显然的二分图
把每个英国飞行员连一条边到外籍飞行员上
用匈牙利算法找到最大匹配
顺便输出存储匹配的数组match就OK
当然也可以用网络流,然而不如二分图好写
(能用匈牙利为什么要用网络流..)
顺便提一下网络流怎么写:
建两个虚拟节点0和n+1,用0节点连接所有的外籍飞行员,用n+1连接所有英国飞行员
然后所有的边边权都为1
最后从0到n+1跑最大流就可以了
以下为匈牙利算法(网络流是不可能有的)
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<vector> using namespace std; inline int read() { register int x=0; int f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; }//快读 int fir[10007],to[10007],from[10007],cnt; inline void add(int &a,int &b) { from[++cnt]=fir[a]; fir[a]=cnt; to[cnt]=b; }//链式前向星存图 int match[107]; //匹配数组(划重点),match[i]表示与 编号为i的英国飞行员 的匹配的外籍飞行员的编号 int n,m,ans; bool vis[107]; inline bool dfs(int x)//如果dfs返回1就表示找到了一种方案(即编号为x的外籍飞行员找到了一个英国飞行员可以匹配) { for(int i=fir[x];i;i=from[i]) { int u=to[i]; if(vis[match[u]]) continue; vis[match[u]]=1; if(!match[u]||dfs(match[u]))//如果这个英国飞行员u还没被匹配,或者与Ta匹配的外籍飞行员可以找另一个人匹配 { match[u]=x;//那么就把这个英国飞行员u的匹配的人改变成x号的外籍飞行员 return 1;//并返回 } } return 0; }//匈牙利算法 int main() { int a,b; cin>>n>>m; while(1) { a=read(); b=read(); if(a==-1&&b==-1) break; add(a,b);//存图 } for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(dfs(i)) ans++;//多找到一种方案就把最大匹配数加1 } cout<<ans<<endl; for(int i=1;i<=m;i++) if(match[i])//如果这个英国飞行员有人可以匹配 printf("%d %d\n",match[i],i); //输出每个外籍飞行员的编号和与之匹配的英国飞行员的编号 return 0; }