【网络流24题-洛谷-P2756】飞行员配对方案问题(二分图最大匹配、最大流)

探讨二战期间英国皇家空军如何通过算法寻找外籍与英国飞行员的最佳配对方案,以实现最多飞机同时出动。该算法基于二分图最大匹配原理,通过构建特殊网络流模型求解最大流,从而确定配对方案。

题目背景

第二次世界大战时期..

题目描述

英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的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 

解题报告:

这道题的很明显是要求二分图的最大匹配,建图方式就是,建立一个超级源点和一个超级汇点,将源点和所有的外籍飞行员连边,将英国飞行员和超级汇点连边,还有就是将给的外籍飞行员和英国飞行员连边,每条边的流量都为1,最后求从超级源点到超级汇点的最大流。最大流的值就是二分图最大匹配的结果。要输出哪些边是匹配的,就遍历所有的外籍飞行员,然后找边的终点是英国飞行员并且流量不为0的点,那么这条边就一定被选过了。(从洛谷还看到一个判断的方法: 判断边是否有流量,即判断反向边的权值是否不为0)。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 1000000007
#define eps 1e-8
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge {
    int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//节点总个数,节点编号从 0 ~ N-1
void init(int n) {
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost) {
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s,int t) {
    queue<int>q;
    for(int i = 0; i < N+1; i++) {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost ) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}
//返回的是最大流,cost 存的是最小费用
int minCostMaxflow(int s,int t,int &cost) {
    int flow = 0;
    cost = 0;
    while(spfa(s,t)) {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}

int main()
{
    int n,m;
    scanf("%d%d",&m,&n);
    init(n+m+2);
    while(1){
        int a,b;
        scanf("%d%d",&a,&b);
        if(a==-1&&b==-1)break;
        addedge(a,b,1,0);
//		add(b,a,1,0);
    }
    for(int i=1;i<=m;i++)
    {
        addedge(0,i,1,0);
    }
    for(int i=m+1;i<=m+n;i++)
    {
        addedge(i,n+m+1,1,0);
        addedge(n+m+1,i,1,0); 
    }
    int c;
    int ans=minCostMaxflow(0,n+m+1,c);
    if(ans==0){
        puts("No Solution!");
    }
    else
    {
        printf("%d\n",ans);
        for(int i=1;i<=m;i++)
        {
            for(int j=head[i];j!=-1;j=edge[j].next){
                if(edge[j].to!=0&&edge[j].to!=n+m+1&&edge[j].flow>0)
                {
                    printf("%d %d\n",i,edge[j].to);
                    break;
                }
            }
        }
    }
    return 0;
} 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值