题面
因为 LOJ 上的版本比较简洁,所以就放这个版本的题面了。
飞行大队有若干个来自各地的驾驶员,专门驾驶一种型号的飞机,这种飞机每架有两个驾驶员,需一个正驾驶员(英国)和一个副驾驶员(外籍)。由于种种原因,例如相互配合的问题,有些驾驶员不能在同一架飞机上飞行,问如何搭配驾驶员才能使出航的飞机最多。
因为驾驶工作分工严格,两个正驾驶员或两个副驾驶员都不能同机飞行。
思路
二分图匹配问题。可以使用匈牙利算法解决,也可用网络流求解。
如果一个英国驾驶员可以配对一个外籍驾驶员,那么就将他们两个连一条边(权值为 $1$ )。
然后定义 S 点和 T 点分别为源点和汇点。因为数据很小,所以这里我将 S 点和 T 点编号分别定为 2333 和 2334。
将 S 与所有英国驾驶员连一条边,T 与所有外籍驾驶员连一条边。
然后求从 S 到 T 的最大流即可。
注意
洛谷版本的题面需要输出配对方案,我们用 $res[i]$ 来表示 $i$ 点的上一个点,在 EK 时更新这个数组即可。
代码
洛谷版本
#include<bits/stdc++.h> using namespace std; long long n,m,last[200005],to[200005],nextt[200005],s[200005],top=1; long long v[200005]; long long res[200005]; struct path{ long long from,edge; }; path p[200005]; void add(int a,int b,int c){ nextt[++top]=last[a]; to[top]=b; s[top]=c; last[a]=top; } bool bfs(long long S,long long T){ memset(v,0,sizeof(v)); memset(p,-1,sizeof(p)); queue<long long> q; q.push(S); v[S]=1; while (!q.empty()){ long long now=q.front(); q.pop(); for (int i=last[now];i;i=nextt[i]){ int j=to[i]; if (v[j]||s[i]<=0){ continue; } p[j].from=now; p[j].edge=i; if (j==T){ return 1; } q.push(j); v[j]=1; } } return 0; } long long EK(long long S,long long T){ long long ans=0; while (bfs(S,T)){ long long minn=2147483647; for (int i=T;i!=S;i=p[i].from){ minn=min(minn,s[p[i].edge]); } ans+=minn; for (int i=T;i!=S;i=p[i].from){ res[p[i].from]=i;//记录配对方案 s[p[i].edge]-=minn; s[p[i].edge^1]+=minn; } } return ans; } int main(){ int S=2333,T=2334; cin>>m>>n; for (int i=1;i<=m;i++){ add(S,i,1); add(i,S,0); } for (int i=1;i<=n;i++){ add(i+n,T,1); add(T,i+n,0); } while (1){ int a,b; cin>>a>>b; if (a==-1&&b==-1){ break; } add(a,b+n,1); add(b+n,a,0); } cout<<EK(S,T)<<endl; for (int i=1;i<=n;i++){ if (res[i]){ cout<<i<<" "<<res[i]-n<<endl; } } }
LOJ 版本
LOJ 上这道题更简单,不需要记录方案。但和洛谷上有一些细节的不同,需要注意。
#include<bits/stdc++.h> using namespace std; long long n,m,last[200005],to[200005],nextt[200005],s[200005],top=1; long long v[200005]; struct path{ long long from,edge; }; path p[200005]; void add(int a,int b,int c){ nextt[++top]=last[a]; to[top]=b; s[top]=c; last[a]=top; } bool bfs(long long S,long long T){ memset(v,0,sizeof(v)); memset(p,-1,sizeof(p)); queue<long long> q; q.push(S); v[S]=1; while (!q.empty()){ long long now=q.front(); q.pop(); for (int i=last[now];i;i=nextt[i]){ int j=to[i]; if (v[j]||s[i]<=0){ continue; } p[j].from=now; p[j].edge=i; if (j==T){ return 1; } q.push(j); v[j]=1; } } return 0; } long long EK(long long S,long long T){ long long ans=0; while (bfs(S,T)){ long long minn=2147483647; for (int i=T;i!=S;i=p[i].from){ minn=min(minn,s[p[i].edge]); } ans+=minn; for (int i=T;i!=S;i=p[i].from){ s[p[i].edge]-=minn; s[p[i].edge^1]+=minn; } } return ans; } int main(){ int S=2333,T=2334; cin>>n>>m; for (int i=1;i<=m;i++){ add(S,i,1); add(i,S,0); } for (int i=1;i<=n;i++){ add(i+n,T,1); add(T,i+n,0); } int a,b; while (cin>>a>>b){ add(a,b+n,1); add(b+n,a,0); } cout<<EK(S,T); }