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

网络流24题的第一题,主要是一些学习的总结

努力做到比大佬们的博客都要简单易懂~!适合初学网络流的同学

问题描述:每一架飞机都需要配备在航行技能和语言上能互相配合的 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!’。
输入
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1

输出
4
1 7
2 9
3 8
5 10

这道题可以说是非常简单的一道题,简言之就在二分图(英国飞行员和外籍飞行员)的两类飞行员之间连边,然后求最大匹配

求最大匹配最常见的方法是匈牙利算法,这里不展开,但二分匹配可以较为简单地转化成网络流问题

我们知道网络流dinic算法求的是在有向图/无向图中,给定每条边最大流量限制的情况下,从源点S汇点T之间的最大总流量。那么,我们可以把二分图的两部分看成点集X(英国飞行员)和点集Y(外籍飞行员),并进行如下操作:

1. 添加两个点源点S汇点T;

2. 从S点集X中的所有点连一条边(无向边的容量均为1);

3. 从点集XT中的所有点连一条边;

当然,点集X和Y之间也要连上边;然后我们可以比较显然地发现,从S到T的最大流就是我们需要的二分图最大匹配!这是因为从S到点集X中的每个点 x_i 都连了一条容量为1的边,这条边尽可能从 x_i 流出去就会得到最大流。而不能增加流量的情况很显然就是最大匹配了。

分析完成!接下来我们就要记住这种方法,然后就可以用在二分图中效率异常优秀(见《算法竞赛进阶指南》)的dinic算法求出最大流。

接着,匹配的情况怎么求呢?要求这个需要了解网络流dinic算法的基本原理(之后的题目也是,如果不会请先看书和看博客了解一下),在求出无向图的最大流以后,相当于每条边的容量减去此时的流量f,反向边(这个也先了解一下,简言之就是编号为 i 的边反向边为编号为 i^1 )加上流量f。因为我们边的容量都是1,所以如果dinic之后二分图上的边的容量=0,那么它一定是最大匹配中的边了。

代码如下:

大家也可以保存下来作为自己的dinic模板

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 10005, maxm = 100005, INF = 0x3f3f3f3f;
struct Edge{
    ll next,to,f;
}edge[maxm*2];
ll n,m,head[maxn],tot;
void INIT(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void insert(int u,int v,int w){
    edge[tot].to = v, edge[tot].f = w, edge[tot].next = head[u], head[u] = tot;
    tot++;
    edge[tot].to = u, edge[tot].f = w, edge[tot].next = head[v], head[v] = tot;
    //有向图反向弧容量初始化为0
    tot++;
}
ll level[maxn];
bool makelevel(ll s,ll t){
    memset(level,0,sizeof(level));
    level[s]=1;
    ll que[maxn];
    ll iq=0;
    que[iq++] = s;
    ll i,k;
    ll top;
    for(i=0; i<iq; i++){
        top = que[i];
        if(top == t) return true;
        for(k=head[top]; k!=-1; k=edge[k].next){
            if(! level[edge[k].to] && edge[k].f){
                que[iq++] = edge[k].to;
                level[edge[k].to] = level[top]+1;
            }
        }
    }
    return false;
}
ll DFS(ll now, ll maxf, ll t){
    if(now == t) return maxf;
    ll ret=0,f;
    ll k;
    for(k = head[now]; k!=-1; k=edge[k].next){
        if(edge[k].f && level[edge[k].to] == level[now] + 1){
            f = DFS(edge[k].to, min(maxf-ret, edge[k].f), t);
            edge[k].f -= f;
            edge[k^1].f += f;
            ret += f;
            if(ret == maxf) return ret;
        }
    }
    return ret;
}

ll DINIC(int s, int t){
    ll ans=0;
    while(makelevel(s,t)) ans += DFS(s,INF,t);
    return ans;
}

int main(){
    scanf("%lld%lld",&m,&n);
    INIT();
    ll u,v;
    while(~scanf("%lld%lld",&u,&v)){
        if(u==-1&&v==-1)break;
        insert(u,v,1);
    }
    for(int i=1;i<=m;i++)
        insert(0,i,1);
    for(int i=m+1;i<=n;i++)
        insert(i,n+1,1);
    printf("%lld\n",DINIC(0,n+1));
    for(ll now=1;now<=n;now++){
        for(ll k=head[now];k!=-1;k=edge[k].next){
            if(edge[k].f==0){
                if(edge[k].to!=n+1)
                    printf("%lld %lld\n",now,edge[k].to);
            }
        }
    }
    return 0;
}
/*
无向图的最大流与有向图的最大流的区别在于 
反向边的流量不是零而是与正向边相等。注意 
这点之后,再打一个Dinic算法模板。考虑到 
数据特别地大,需要进行当前弧优化。即在每 
一次找增广路前进行:
*/ 
/*
8 9
1 2 2
1 3 10
2 4 2
3 4 10
4 5 16
5 6 10
5 7 10
6 8 10
7 8 10
*/

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络算法可以用来解决最大最小割问题,而飞行员配对问题可以转化成最大问题。下面是一个基于 Python 的网络算法求解飞行员配对方案问题的示例代码: ```python from collections import defaultdict class Graph: def __init__(self): self.graph = defaultdict(dict) def add_edge(self, u, v, w): self.graph[u][v] = w def bfs(self, s, t, parent): visited = set() queue = [s] visited.add(s) while queue: u = queue.pop(0) for v in self.graph[u]: if v not in visited and self.graph[u][v] > 0: queue.append(v) visited.add(v) parent[v] = u return True if t in visited else False def max_flow(self, source, sink): parent = {} max_flow = 0 while self.bfs(source, sink, parent): path_flow = float('inf') s = sink while s != source: path_flow = min(path_flow, self.graph[parent[s]][s]) s = parent[s] max_flow += path_flow v = sink while v != source: u = parent[v] self.graph[u][v] -= path_flow self.graph[v][u] += path_flow v = parent[v] return max_flow def pilot_pairing(pilots, planes): num_pilots = len(pilots) num_planes = len(planes) graph = Graph() source = 's' sink = 't' for i in range(num_pilots): graph.add_edge(source, 'P{}'.format(i), 1) for j in range(num_planes): if pilots[i][0] <= planes[j][0] and pilots[i][1] >= planes[j][1]: graph.add_edge('P{}'.format(i), 'A{}'.format(j), 1) graph.add_edge('A{}'.format(i), sink, 1) return graph.max_flow(source, sink) # 示例用法 pilots = [(1, 5), (2, 4), (3, 6), (2, 5)] planes = [(1, 4), (2, 5), (3, 6)] print(pilot_pairing(pilots, planes)) # 输出为 3 ``` 在上面的示例代码中,我们定义了一个 `Graph` 类来表示图,其中 `add_edge` 方法用于添加边,`bfs` 方法用于执行广度优先搜索,`max_flow` 方法用于计算最大。 在 `pilot_pairing` 函数中,我们首先创建了一个 `Graph` 对象,然后为源点和汇点分别添加一条边,并遍历所有的飞行员和飞机,如果某个飞行员可以驾驶某个飞机,则在他们之间添加一条边。最后调用 `max_flow` 方法计算最大。 在 `max_flow` 方法中,我们首先执行广度优先搜索来寻找一条增广路径,然后计算路径上的最小剩余容量,更新路径上的边的量,并更新最大的值。重复执行这个过程,直到没有增广路径为止。最后返回最大的值。 在上面的示例中,我们输入了 4 个飞行员和 3 个飞机,输出得到了最大为 3,说明有 3 对飞行员和飞机可以配对成功。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值