传送门

二分图

稍微讲一下二分图:

就是一个图分成两个部分A和B,这个图的无向边只连接不同的两个部分,即边不能在A或B的内部自己连自己

然后问你最大能找到多少匹配

匹配是指 A中的一个点与B中的一个点有边 并且 这两个点都还没有与其它点匹配

这种问题一般用匈牙利算法:

  匈牙利算法的思想很简单

  对于一个点a能否找到另一个点匹配

  深搜a

  如果与a相连的这个点b还没匹配

  那么毫无问题a可以和b匹配

  但是如果b有匹配了

  没事,去问一下与b匹配的点能不能找其它的点匹配

  方法就是继续深搜(深搜与b匹配的点能否找到再另一个点匹配......)

  然后一直下去

  如果最终找到了

  回来,更新匹配


 

本题是十分显然的二分图

把每个英国飞行员连一条边到外籍飞行员上

用匈牙利算法找到最大匹配

顺便输出存储匹配的数组match就OK

当然也可以用网络流,然而不如二分图好写

(能用匈牙利为什么要用网络流..)

顺便提一下网络流怎么写:

建两个虚拟节点0和n+1,用0节点连接所有的外籍飞行员,用n+1连接所有英国飞行员

然后所有的边边权都为1

最后从0到n+1跑最大流就可以了

以下为匈牙利算法(网络流是不可能有的)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
    register int x=0; int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}//快读
int fir[10007],to[10007],from[10007],cnt;
inline void add(int &a,int &b)
{
    from[++cnt]=fir[a];
    fir[a]=cnt;
    to[cnt]=b;
}//链式前向星存图
int match[107];
//匹配数组(划重点),match[i]表示与 编号为i的英国飞行员 的匹配的外籍飞行员的编号
int n,m,ans;
bool vis[107];
inline bool dfs(int x)//如果dfs返回1就表示找到了一种方案(即编号为x的外籍飞行员找到了一个英国飞行员可以匹配)
{
    for(int i=fir[x];i;i=from[i])
    {
        int u=to[i];
        if(vis[match[u]]) continue;
        vis[match[u]]=1;
        if(!match[u]||dfs(match[u]))//如果这个英国飞行员u还没被匹配,或者与Ta匹配的外籍飞行员可以找另一个人匹配
        {
            match[u]=x;//那么就把这个英国飞行员u的匹配的人改变成x号的外籍飞行员
            return 1;//并返回
        }
    }
return 0;
}//匈牙利算法
int main()
{
    int a,b;
    cin>>n>>m;
    while(1)
    {
        a=read(); b=read();
        if(a==-1&&b==-1) break;
        add(a,b);//存图
    }
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i))
          ans++;//多找到一种方案就把最大匹配数加1
    }
    cout<<ans<<endl;
    for(int i=1;i<=m;i++)
        if(match[i])//如果这个英国飞行员有人可以匹配
            printf("%d %d\n",match[i],i); //输出每个外籍飞行员的编号和与之匹配的英国飞行员的编号
    return 0;
}