飞行员配对方案问题(二分图最大匹配)

      本题一个英国飞行员可以和特定的一个外国飞行员搭配,又要求求最佳的选人方式使选到的人最多,所以一看就是二分图的最大匹配,可以用匈牙利算法搞定。当然也可以建立网络流模型,首先建立源点S和汇点T,源点向所有英国飞行员连流量为1的边,再把所有英国飞行员和能与之搭配的外国飞行员间连一条流量为1的边,最后把所有外国飞行员向汇点T连一条流量为1的边,此时该模型的最大流等于二分图的最大匹配数。又因为要输出一种方案,如果用匈牙利算法就开一个flag数组进行标记,记录二分图左边能匹配右边的哪一个,最后for一遍输出。如果是网络流算法,就for一遍英国飞行员个数,如果它到右边有外国飞行员相连,并且能流的流量为0,说明是满流,说明是一种方案,最后输出就行啦。

匈牙利算法:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>

#define M 211

using namespace std;

int n,m;
int dis[M][M],fy[M],mat[M];
int flag[M];

void init()
{
    scanf("%d%d",&n,&m);
    memset(dis,0,sizeof(dis));
    memset(mat,-1,sizeof(mat));
    memset(flag,-1,sizeof(flag));
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF&&x!=-1&&y!=-1)
        dis[x][y]=1;
}

bool crosspath(int u)
{
    for(int i=m+1;i<=n;i++)
    {
        if(dis[u][i]==1&&fy[i]==0)
        {
            fy[i]=1;
            if(mat[i]==-1||crosspath(mat[i]))
            {
                flag[u]=i;
                mat[i]=u;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        if(flag[i]==-1)
        {
            memset(fy,0,sizeof(fy));
            ans+=crosspath(i);
        }
    }
    return ans;
}

int main()
{
    freopen("flyer.in","r",stdin);
    freopen("flyer.out","w",stdout);
    init();
    int sum=hungary();
    if(sum==0)
        printf("No Solution!\n");
    else
    {
        printf("%d\n",sum);
        /*for(int i=1;i<=m;i++)
        {
            if(flag[i]!=-1)
                printf("%d %d\n",i,flag[i]);
        }*/
        //输出匹配方案
    }

    return 0;
}



网络流模型:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>

#define M 211*211
#define INF 0x7fffffff

using namespace std;

int n,m;
int num=0;
int p[M];
int q[4*M];
int level[M];

struct node
{
    int a;
    int b;
    int w;
    int next;
}str1[M];

void add(int x,int y,int z)
{
    str1[num].a=x;
    str1[num].b=y;
    str1[num].w=z;
    str1[num].next=p[x];
    p[x]=num++;
}

void init()
{
    scanf("%d%d",&m,&n);
    memset(p,-1,sizeof(p));
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF&&x!=-1&&y!=-1)
    {
        add(x,y,1);
        add(y,x,0);
    }
}

bool makelevel(int st,int ed)
{
    memset(level,0,sizeof(level));
    level[st]=1;
    int l=0,r=0;
    q[r++]=st;
    while(l<r)
    {
        int k=q[l++];
        if(k==ed)
            return true;
        for(int i=p[k];i!=-1;i=str1[i].next)
        {
            int v=str1[i].b;
            if(!level[v]&&str1[i].w)
            {
                level[v]=level[k]+1;
                q[r++]=v;
            }
        }
    }
    return false;
}

int dfs(int now,int maxf,int ed)
{
    if(now==ed)
        return maxf;
    int ret=0,sf;
    for(int i=p[now];i!=-1;i=str1[i].next)
    {
        int v=str1[i].b;
        if(str1[i].w&&level[v]==level[now]+1)
        {
            int minx=min(maxf-ret,str1[i].w);
            sf=dfs(v,minx,ed);
            str1[i].w-=sf;
            str1[i^1].w+=sf;
            ret+=sf;
            if(ret==maxf)
                return ret;
        }
    }
    return ret;
}

int dinic(int st,int ed)
{
    int ans=0;
    while(makelevel(st,ed))
        ans+=dfs(st,INF,ed);
    return ans;
}

int main()
{
    //freopen("flyer.in","r",stdin);
    //freopen("flyer.out","w",stdout);
    init();
    for(int i=1;i<=m;i++)
    {
        add(200,i,1);
        add(i,200,0);
    }
    for(int i=m+1;i<=n;i++)
    {
        add(i,300,1);
        add(300,i,0);
    }
    printf("%d\n",dinic(200,300));
    for(int i=1;i<=m;i++)
    {
        for(int j=p[i];j!=-1;j=str1[j].next)
        {
            if(str1[j].w==0&&str1[j].b!=0&&j%2==0)
                printf("%d %d\n",i,str1[j].b);
        }

    }
    return 0;
}


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值