题目背景

第二次世界大战时期..

题目描述

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

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

输入输出格式

输入格式:

第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行员总数(n<100);m 是外籍飞行员数(m<=n)。外籍飞行员编号为 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 

Solution:

本题有两种方法。

方法一:明眼看,这道题一道裸的二分图最大匹配的题目,只需要判断并输出前趋数组pre和对应的i就OK了。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=105;
 4 inline int gi()
 5 {
 6     char x=getchar();int a=0,f=1;
 7     while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
 8     while(x>='0'&&x<='9'){a=a*10+x-'0';x=getchar();}
 9     return a*f;
10 }
11 int n,m,s,u,v;
12 struct edge
13 {
14     int v,ne;
15 }e[N*N<<1];
16 int h[N],cnt;
17 inline void ins(int u,int v){
18     e[++cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
19 }
20 int le[N];
21 bool vis[N];
22 bool find(int u){
23     for(int i=h[u];i;i=e[i].ne){
24         int v=e[i].v;
25         if(!vis[v]){
26             vis[v]=1;
27             if(!le[v]||find(le[v])){
28                 le[v]=u;
29                 return 1;
30             }
31         }
32     }
33     return 0;
34 }
35 int ans;
36 int main(){
37     n=gi();m=gi();
38     while(1){u=gi();v=gi();if(v==-1)break;ins(u,v);}
39     for(int i=1;i<=m;i++){
40         memset(vis,0,sizeof(vis));
41         if(find(i)) ans++;    
42     }
43     if(ans==0)printf("No Solution!");
44     else {
45     printf("%d\n",ans);
46     for(int i=n+1;i<=m;i++)
47     if(le[i])printf("%d %d\n",le[i],i);
48     }
49     return 0;
50 }

 

方法二:学了网络流后,这道题也能用最大流做了。我们只需建立两个虚点S和T,S连向其中一个点集建n条边,T连向另一个点集建m条边,上述的边设置容量为1,然后照常对两个点集进行建边就行了(边设置容量为1),最后跑最大流就行了,当然也得记录并输出前趋。

代码(1):(非手打,搬运自巨佬YZK的ISAP)

 1 #include<bits/stdc++.h>
 2 #define sins(x,y,z) ins(x,y,z),ins(y,x,0)
 3 #define INF (0x7fffffff)
 4 #define DEBUG printf("Passing %d in [%s]...\n",__LINE__,__FUNCTION__)
 5 #define _rg register
 6 using namespace std;
 7 
 8 template <typename T> void gn(T &x){
 9     x=0;
10     T pl=1;
11     char ch=getchar();
12     while(ch<'0' || ch>'9') pl=(ch=='-'?-1:1), ch=getchar();
13     while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
14     x*=pl;
15 }
16 
17 const int N=1100,M=2e5+10;
18 int h[N],to[M],nexp[M],W[M],p=2;
19 inline void ins(int a,int b,int w){
20     nexp[p]=h[a], h[a]=p, to[p]=b, W[p]=w, p++;
21 }
22 
23 int m,n;
24 int fl[N],rem[N];
25 int ISAP(int x,int op){
26     if(x==n+2) return op;
27     int flow=0,d,minfl=-1;
28     // DEBUG;
29     for(_rg int u=h[x];u;u=nexp[u])
30         if(W[u]){
31             if(fl[to[u]]+1==fl[x]) {
32                 d=ISAP(to[u], op-flow<W[u]?op-flow:W[u]);
33                 flow+=d, W[u]-=d, W[u^1]+=d;
34                 if(flow==op || fl[n+1]==-1) return flow;
35             }
36             if((minfl==-1 || minfl>fl[to[u]]+1) && fl[to[u]]!=-1) minfl=fl[to[u]]+1;
37         }
38     // DEBUG;
39     if(!flow){
40         if(!(--rem[fl[x]])) fl[n+1]=-1;
41         fl[x]=minfl;
42         if(fl[x]!=-1) rem[fl[x]]++;
43     }
44     return flow;
45 }
46 
47 int main(){
48     // ios::sync_with_stdio(false);
49     gn(m),gn(n);
50     int u,v;
51     while(gn(u),gn(v),u!=-1) sins(u,v,1);
52     for(int i=1;i<=m;i++) sins(n+1,i,1);
53     for(int i=m+1;i<=n;i++) sins(i,n+2,1);
54 
55     queue<int> qu;
56     qu.push(n+2);
57     fl[n+2]=1, rem[1]++;
58     while(!qu.empty()){
59         int na=qu.front();
60         qu.pop();
61         for(int u=h[na];u;u=nexp[u])
62             if(!W[u] && !fl[to[u]]){
63                 fl[to[u]]=fl[na]+1;
64                 rem[fl[na]+1]++;
65                 qu.push(to[u]);
66             }
67     }
68     // DEBUG;
69     int ans=0;
70     if(!fl[n+1]) cout<<"No Solution!"<<endl;
71     while(fl[n+1]!=-1) ans+=ISAP(n+1,INF);
72     cout<<ans<<endl;
73     for(int i=1;i<=m;i++){
74         for(int u=h[i];u;u=nexp[u])
75             if(to[u]!=n+1 && !W[u]) {cout<<i<<' '<<to[u]<<endl; break;}
76     }
77     return 0;
78 }

 代码2:(纯手打Dinic)

 

 1 #include<bits/stdc++.h>
 2 #define il inline
 3 using namespace std;
 4 const int N=105,inf=23333333;
 5 int m,n,dis[N],h[N],cnt=1,ans,s,t;
 6 struct edge{
 7 int to,net,cap;
 8 }e[100005];
 9 il void add(int u,int v,int w)
10 {
11   e[++cnt].to=v,e[cnt].net=h[u],h[u]=cnt,e[cnt].cap=w;
12   e[++cnt].to=u,e[cnt].net=h[v],h[v]=cnt,e[cnt].cap=0;
13 }
14 queue<int>q;
15 il bool bfs()
16 {
17   memset(dis,-1,sizeof(dis));
18   dis[s]=0;
19   q.push(s);
20   while(!q.empty())
21   {
22     int u=q.front();
23     q.pop();
24     for(int i=h[u];i;i=e[i].net)
25     {
26     int v=e[i].to;
27     if(dis[v]==-1&&e[i].cap>0){dis[v]=dis[u]+1;q.push(v);}
28     }
29   }
30   return dis[t]!=-1;
31 }
32 il int dfs(int u,int op)
33 {
34   if(u==t)return op;
35   int flow=0,tmp=0;
36   for(int i=h[u];i;i=e[i].net)
37   {
38     int v=e[i].to;
39     if(dis[v]==dis[u]+1&&e[i].cap>0){
40     tmp=dfs(v,min(op,e[i].cap));
41     if(!tmp)continue;
42     op-=tmp;flow+=tmp;
43     e[i].cap-=tmp;e[i^1].cap+=tmp;
44     if(!op)break;
45     }
46   }
47   if(!flow)dis[u]=-1;
48   return flow;
49 }
50 int main()
51 {
52   scanf("%d%d",&m,&n);
53   s=0,t=n+1;
54   int u,v,w;
55   while(1){
56     scanf("%d%d",&u,&v);
57     if(u==-1&&v==-1)break;
58     add(u,v,1);
59   }
60   for(int i=1;i<=m;i++)add(s,i,1);
61   for(int i=m+1;i<=n;i++)add(i,n+1,1);
62   while(bfs())ans+=dfs(s,inf);
63   if(!ans)printf("No Solution!");
64   else {
65   printf("%d\n",ans);
66   for(int i=1;i<=m;i++)
67   for(int j=h[i];j;j=e[j].net)
68   if(!e[j].cap&&e[j].to!=0)printf("%d %d\n",i,e[j].to);
69   }
70   return 0;
71 }