题目链接:P2756 飞行员配对方案问题
一共有 n n n 个飞行员,其中有 m m m 个外籍飞行员和 ( n − m ) (n - m) (n−m) 个英国飞行员,外籍飞行员从 1 1 1 到 m m m 编号,英国飞行员从 m + 1 m + 1 m+1 到 n n n 编号。 对于给定的外籍飞行员与英国飞行员的配合情况,找到最大匹配数,并输出所有成功匹配的成对飞行员编号。
匹配问题之一,典型的二分图最大匹配问题,这里我们考虑用网络流解。
我们定义一条边的流量为
1
1
1就是能匹配,那么最终流到终点的流量就是最大匹配数。
给
0
0
0号节点与所有外籍飞行员建一条流量为
1
1
1的边
所有英国飞行员与
n
+
1
n + 1
n+1号节点建一条流量为
1
1
1的边
再根据题所给的外籍飞行员与英国飞行员建流量为
1
1
1的边
从
0
0
0号节点到
n
+
1
n + 1
n+1号节点的最大流量即为最大匹配数,所有正向边中流量变成
0
0
0的边为匹配上的一对(原本为
1
1
1代表能匹配,若匹配上等于有一个容量为
1
1
1的流经过,会使得边容量
−
1
-1
−1,此时容量就为
0
0
0了)
使用Dinic算法解题
#include
#include
#include
using namespace std;
const int N = 110, M = 10010;
struct edge{
int to,nex,w;
}e[M * 2];
int n,m,head[N],tot = 1;
void add(int to,int from,int w){
e[++ tot].w = w;
e[tot].to = to;
e[tot].nex = head[from];
head[from] = tot;
}
int dep[N];
bool bfs()
{
queue<int>q;
q.push(0);
for(int i = 1; i <= n + 1; i ++) dep[i] = 0;
dep[0] = 1;
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = head[u]; i; i = e[i].nex){
int v = e[i].to;
if(e[i].w && !dep[v]){
dep[v] = dep[u] + 1;
if(v == n + 1) return true;
q.push(v);
}
}
}
return dep[n + 1];
}
int dfs(int u, int inflow)
{
if(u == n + 1)
return inflow;
int outflow = 0;
for(int i = head[u]; i && inflow; i = e[i].nex){
int v = e[i].to;
if(e[i].w && dep[v] == dep[u] + 1){
int flow = dfs(v, min(e[i].w, inflow));
e[i].w -= flow;
e[i ^ 1].w += flow;
inflow -= flow;
outflow += flow;
}
}
if(outflow == 0) dep[u] = 0;
return outflow;
}
int main()
{
scanf("%d%d",&m,&n);
int u,v;
for(int i = 1; i <= m; i ++){
add(0, i, 0);
add(i, 0, 1);
}
for(int i = m + 1; i <= n; i ++){
add(i, n + 1, 0);
add(n + 1, i, 1);
}
while(scanf("%d%d",&u,&v) != EOF){
if(u == -1 && v == -1) break;
if(u > v) swap(u, v);
add(u, v, 0);
add(v, u, 1);
}
int sum = 0;
while(bfs()){
sum += dfs(0, 10000);
}
printf("%d\n",sum);
for(int i = 1; i <= m; i ++){
for(int j = head[i]; j; j = e[j].nex){
if(!e[j].w && e[j].to) printf("%d %d\n",i, e[j].to);
}
}
return 0;
}
网络流练习题题单及题解:网络流练习题单