飞行员配对方案问题

题目背景

第二次世界大战时期..

题目描述

英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

输入输出格式

输入格式:

第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行员总数(n<100);m 是外籍飞行员数。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。

接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。最后以 2个-1 结束。

输出格式:

第 1 行是最佳飞行员配对方案一次能派出的最多的飞机数 M。接下来 M 行是最佳飞行员配对方案。每行有 2个正整数 i 和 j,表示在最佳飞行员配对方案中,飞行员 i 和飞行员 j 配对。如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’。

输入输出样例

输入样例#1:
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
输出样例#1:
4
1 7
2 9
3 8
5 10 
思路:网络流
代码实现:
 1 #include<cstdio>
 2 #include<cstring>
 3 int n,m,ans,cp[110];
 4 int a,b,c;
 5 int h[110],hs;
 6 struct edge{int s,n,v,r;}e[12100];
 7 int head,tail,d[110];
 8 struct queue{int s,d;}q[110];
 9 int min(int x,int y){return x<y?x:y;}
10 bool bfs(int s,int t){
11     memset(d,0,sizeof(d));
12     head=tail=0;
13     q[head++]=(queue){s,1},d[s]=1;
14     while(head>tail){
15         a=q[tail].s,b=q[tail++].d;
16         for(int i=h[a];i;i=e[i].n) if(!d[e[i].s]&&e[i].v){
17             q[head++]=(queue){e[i].s,b+1};
18             d[e[i].s]=b+1;
19             if(e[i].s==t) return true;
20         }
21     }
22     return false;
23 }
24 int ap(int k,int t,int v){
25     if(!v||k==t) return v;
26     int act=0;
27     for(int i=h[k];i;i=e[i].n) if(e[i].v&&d[e[i].s]==d[k]+1){
28         int ret=ap(e[i].s,t,min(v-act,e[i].v));
29         if(ret){
30             e[i].v-=ret,e[e[i].r].v+=ret,act+=ret;
31             if(k>0&&k<=m&&e[i].s>m&&e[i].s<=n) cp[k]=e[i].s;
32         }
33     }
34     return act;
35 }
36 bool Dinic(int s,int t){
37     if(!bfs(s,t)) return 0;
38     ans+=ap(s,t,m);
39 }
40 int main(){
41     scanf("%d%d",&m,&n);
42     while(scanf("%d%d",&a,&b)!=EOF){
43         if(a==-1&&b==-1) break;
44         e[++hs]=(edge){b,h[a],1,hs+1},h[a]=hs;
45         e[++hs]=(edge){a,h[b],0,hs-1},h[b]=hs;
46     }
47     for(int i=1;i<=m;i++) e[++hs]=(edge){i,h[0],1},h[0]=hs;
48     for(int i=m+1;i<=n;i++) e[++hs]=(edge){n+1,h[i],1},h[i]=hs;
49     while(Dinic(0,n+1));
50     printf("%d
",ans);
51     for(int i=1;i<=n;i++) if(cp[i]) printf("%d %d
",i,cp[i]);
52     return 0;
53 } 

一个小发现,用网络流做二分图匹配不需要给源点s和汇点t引出的边建反边,有心人可以尝试证明一下。

另外,不要尝试codevs上的,评测方法有坑,除非特定顺序才行。

题目来源:洛谷