【ACM】网络流24题——洛谷P2756飞行员配对方案问题

题目链接:https://www.luogu.org/problemnew/show/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 

这道题是一道典型的求二分图最大匹配的问题,可以用匈牙利算法求解,输出匹配方案的时候直接输出linker[i]就可以。但是它放在了网络流24题里,就用Dinic算法做了一遍。

建图的时候如果将一个飞行员看做一个汇点,就会有很多个源点和汇点。所以要建立一个超级源点和一个超级汇点,加边的时候将流量都设为1,左边的节点均加上一条和源点相连的边,右边的节点均加上一条和汇点相连的边。匹配结果即为正向边流量为0,反向边流量为1的边。

代码如下:

Hungary:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=510;
int uN,vN;
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
    for(int v=0;v<vN;v++)
    {
        if(g[u][v]&&!used[v])
        {
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v]))
            {
                linker[v]=u;//linker右边==左边
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(int u=0;u<uN;u++)
    {
        memset(used,false,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n,m;
    int i,j;
    scanf("%d%d",&m,&n);
    while(~scanf("%d%d",&i,&j))
    {
        if(i==-1&&j==-1)
            break;
        g[i-1][j-m-1]=1;
    }
    uN=m,vN=n-m;
    int ans=hungary();
//    if(ans==0)
//        printf("No solution!");
//    else
//    {
        printf("%d\n",ans);
        for(int a=0;a<n-m;a++)
        {
            if(linker[a]!=-1)
            printf("%d %d\n",linker[a]+1,a+m+1);
        }
    //}
    return 0;
}

Dinic:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cstdlib>
#include<string>
typedef long long ll;
typedef unsigned long long LL;
using namespace std;
const int INF=0x3f3f3f3f;
const double pi=acos(-1.0);
const double eps=0.00000001;
const int N=60100;
struct node{
    int to,next;
    int flow;
}edge[N*10];
int head[N];
int dis[N];
int tot;
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void add(int u,int v,int flow){
    edge[tot].to=v;
    edge[tot].flow=flow;
    edge[tot].next=head[u];
    head[u]=tot++;

    edge[tot].to=u;
    edge[tot].flow=0;
    edge[tot].next=head[v];
    head[v]=tot++;
}
int BFS(int s,int t){
    queue<int>q;
    memset(dis,-1,sizeof(dis));
    q.push(s);
    dis[s]=0;
    while(q.empty()==0){
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(dis[v]==-1&&edge[i].flow){
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    if(dis[t]==-1)return 0;
    return 1;
}
int DFS(int s,int t,int flow){
    if(s==t)return flow;
    int ans=0;
    for(int i=head[s];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(edge[i].flow&&dis[v]==dis[s]+1){
            int f=DFS(v,t,min(flow-ans,edge[i].flow));
            edge[i].flow=edge[i].flow-f;
            edge[i^1].flow=edge[i^1].flow+f;
            ans=ans+f;
            if(flow==ans)return flow;
        }
    }
    if(ans==0)dis[s]=-1;
    return ans;
}
int Dinc(int s,int t){
    int flow=0;
    while(BFS(s,t)){
        flow+=DFS(s,t,INF);
    }
    return flow;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int m,n;
    scanf("%d%d",&m,&n);
    int a,b;
    init();
    while(~scanf("%d%d",&a,&b))
    {
        if(a==-1&&b==-1)
            break;
        add(a,b,1);
    }
    for(int i=1;i<=m;i++)
        add(0,i,1);
    for(int i=m+1;i<=n;i++)
        add(i,n+1,1);
    printf("%d\n",Dinc(0,n+1));
    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].flow==0&&edge[j^1].flow==1)
                printf("%d %d\n",i,edge[j].to);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41279172/article/details/97024503