网络流二十四题:飞行员配对方案问题

飞行员配对方案问题

题目背景

第二次世界大战期间,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的两名飞行员,其中一名是英国飞行员,另一名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。
题目描述
一共有 n个飞行员,其中有 mm 个外籍飞行员和 (n - m)(n−m) 个英国飞行员,外籍飞行员从 11 到 mm 编号,英国飞行员从 m + 1 到 n编号。 对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。
输入格式
输入的第一行是用空格隔开的两个正整数,分别代表外籍飞行员的个数 m 和飞行员总数 n。
从第二行起到倒数第二行,每行有两个整数 u, v代表外籍飞行员 u 可以和英国飞行员 v 配合。
输入的最后一行保证为 -1 -1,代表输入结束。
输出格式
本题存在 Special Judge。
请输出能派出最多的飞机数量,并给出一种可行的方案。
输出的第一行是一个整数,代表一次能派出的最多飞机数量,设这个整数是 kk。
第 22 行到第 k + 1 行,每行输出两个整数 u, v,代表在你给出的方案中,外籍飞行员 u 和英国飞行员 v 配合。这 k 行的 u 与 v应该互不相同。
输入输出样例
输入 #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
说明/提示
【数据范围与约定】
对于 100%100% 的数据,保证 1≤m≤n<100, m < v,1≤u≤m<v≤n,同一组配对关系只会给出一次。
【提示】
请注意输入的第一行先读入 m,再读入 n。

解题思路

作为网络流二十四题中的模板题,本题使用可以用最大流算法来解决(二分图匈牙利算法为另一种解法),利用网络流解法中,题目中没有一个合适的源点与汇点,需要自己建立一个总的源点与汇点,同时将外国连接英国的边设为1,反向设为0,与源点和和汇点相连的边,应保持与题目中一样的设置边权的方式,这样利用网络流求出最大流就是的得到的二分图最大匹配。

AC代码

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=110;
const int inf=0x3f3f3f3f;

struct node
{
    /* data */
    int v,nxt,w; 
}edge[30100];
int head[maxn];
int cnt=1;
inline void add(int u,int v,int w){
    edge[++cnt].v=v;
    edge[cnt].w =w;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}
int dep[maxn];
bool vis[maxn];
int n,m,res;
int ma[maxn],mb[maxn];
int s,t;
queue<int> q;
void bfs(){
    for(int i=1;i<=n+2;i++){
        vis[i]=0;
    }
    dep[s]=1;
    vis[s]=1;
    q.push(s);
  //  cout<<s<<endl;;
    while (!q.empty())
    {
        int now=q.front();
        q.pop();
        int next=head[now];
       // cout<<next<<endl;
        while(next){
            int v=edge[next].v;
            int w=edge[next].w;
           // cout<<v<<endl;
            if(w!=0&&vis[v]==0){
                dep[v]=dep[now]+1;
               // cout<<v<<' '<<dep[v]<<endl;
                vis[v]=1;
                q.push(v);
            }
            next=edge[next].nxt;
        }
    }
    return ;
    
}
int dfs(int x,int limit){
    if(x==t) return limit;
    int next=head[x];
    while(next){
        int v=edge[next].v;
        int w=edge[next].w;
        if(dep[v]==dep[x]+1&&w!=0){
            int del=dfs(v,min(w,limit));
          //  cout<<del<<endl;
            if(del){
                edge[next].w-=del;
                edge[next^1].w+=del;
                if(x!=s&&v!=t){
                    ma[x]=v;
                    mb[v]=x;
                }
                return del;
            }
        }
        next=edge[next].nxt;
    }
    return 0;
}
int main(){
    cin>>m>>n;
   
    while(1){
        int a,b;
        cin>>a>>b;
        if(a==-1&&b==-1) break;
        add(a,b,1);
        add(b,a,0);
    }
     s=n+1,t=n+2;//建立源汇点
    for(int i=1;i<=m;i++){
        add(s,i,1);
        add(i,s,0);
    }
    for(int i=m+1;i<=n;i++){
        add(i,t,1);
        add(t,i,0);
    }
    bfs();
    
    while(vis[t]){
        while (1)
        {
           int p=dfs(s,inf);
           if(p==0) break;
           else res+=p;
        }
        bfs();//cout<<1<<endl;
    }
    printf("%d\n",res);
    for(int i=1;i<=m;i++){
       // cout<<i<<' '<<ma[i]<<mb[i]<<' '<<mb[ma[i]]<<mb[ma[i]]<<endl;
        if(i==mb[ma[i]]){
            printf("%d %d\n",i,ma[i]);
        }
        else if(i==ma[mb[i]]){
            printf("%d %d\n",i,mb[i]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值