网络流24题之T1——飞行员配对方案问题

问题描述:
第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出
的每一架飞机都需要配备在航行技能和语言上能互相配合的 2 名飞行员, 其中 1 名是英国飞
行员,另 1 名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英
国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的
外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空
军一次能派出最多的飞机。
´编程任务:
对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,
使皇家空军一次能派出最多的飞机。
´数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n。n 是皇家空军的飞行
员总数(n<100);m 是外籍飞行员数。外籍飞行员编号为 1~m;英国飞行员编号为 m+1~n。
接下来每行有 2 个正整数 i 和 j,表示外籍飞行员 i 可以和英国飞行员 j 配合。文件最后以 2个-1 结束。
´结果输出:
程序运行结束时,将最佳飞行员配对方案输出到文件 output.txt 中。第 1 行是最佳飞行
员配对方案一次能派出的最多的飞机数 M。接下来 M 行是最佳飞行员配对方案。每行有 2
个正整数 i 和 j,表示在最佳飞行员配对方案中,飞行员 i 和飞行员 j 配对。
如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’ 。
输入文件示例 input.txt
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1
输出文件示例 output.txt
4
1 7
2 9
3 8
5 10 

题解:这道题是很明显的二分图最大匹配,匈牙利算法可解,不会的话网络流的最大流也是可以的。

建图方式:建超级源s以及超级汇t,s到x子集连一条容量为1的边,x子集到y子集原来已有的边不变,容量为无限大,y子集到t连一条容量为1的边

方法:最大流

P.S.这道题还要输出方案,这个好办,把x和y之间流量为1的边输出即可

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <float.h>
using namespace std;

const int Maxm=105;
const int oo=2147483647;

int s,i,u,v,t,len,n,m;
int head[Maxm];
int cur[Maxm];
int d[Maxm];
int vis[Maxm];

struct edge{
    int u,v,c,f,next;
    edge(){
        u=v=c=f=0;next=-1;
    }
    edge(int x,int y,int z,int a,int b){
        u=x;v=y;c=z;f=a;next=b;
    }
} e[1000005];

void add(int u,int v,int c,int f){
    len++;
    e[len]=edge(u,v,c,f,head[u]);
    head[u]=len;
}

int bfs(){
    int i,x;
    queue <int> q;
    q.push(s);
    for(i=1;i<=n+m+2;i++)
        vis[i]=0;
    d[s]=1;
    vis[s]=1;
    while(!q.empty()){
        x=q.front();
        q.pop();
        for(i=head[x];i!=-1;i=e[i].next)
            if(!vis[e[i].v]&&e[i].f<e[i].c){
                q.push(e[i].v);
                vis[e[i].v]=1;
                d[e[i].v]=d[x]+1;
            }
    }
    return vis[t];
}

int dfs(int x,int a){
    if(x==t||a==0)
        return a;
    int f,flow=0;
    int &i=cur[x];
    for(;i!=-1;i=e[i].next)
        if(d[e[i].v]==d[x]+1&&
           (f=dfs(e[i].v,min(a,e[i].c-e[i].f)))){
            flow+=f;
            e[i].f+=f;
            e[i^1].f-=f;
            a-=f;
            if(a==0)
                break;
        }
    return flow;
}

int dinic(){
    int flow=0;
    while(bfs()){
        for(i=1;i<=n+m+2;i++)
            cur[i]=head[i];
        flow+=dfs(s,oo);
    }
    return flow;
}

int main(){
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    scanf("%d %d",&n,&m);
    len=-1;
    for(i=1;i<=n+m+2;i++)
        head[i]=-1;
    for(i=1;i<=n;i++){
        add(1,1+i,1,0);
        add(1+i,1,0,0);
    }
    for(i=1;i<=m;i++){
        add(1+n+i,n+m+2,1,0);
        add(n+m+2,1+n+i,0,0);
    }
    while(1){
        scanf("%d %d",&u,&v);
        if(u==-1&&v==-1)
            break;
        add(u+1,v+1+n,oo,0);
        add(v+1+n,u+1,0,0);
    }
    s=1;t=n+m+2;
    printf("%d\n",dinic());
    //for(i=0;i<=len;i++)
    //    printf("%d:%d %d %d %d\n",i,e[i].u,e[i].v,e[i].c,e[i].f);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值