【二分图匹配】完美的牛栏

题外话 牛这个东西 除了usaco之外 还有出题人会用吗。。。

【题目描述】
农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术。不幸的是,由于工程问题,每个牛栏都不一样。第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在她们喜欢的那些牛栏中产奶。上个星期,农夫约翰刚刚收集到了奶牛们的爱好的信息(每头奶牛喜欢在哪些牛栏产奶)。一个牛栏只能容纳一头奶牛,当然,一头奶牛只能在一个牛栏中产奶。
给出奶牛们的爱好的信息,计算最大分配方案。

【问题分析】
很简单的二分图匹配 那么就在这里讲一下什么叫做二分图匹配

二分图:如果一张无向图可以被分成两个点集,使得点集内部的点之间没有边,则称这张图是一个二分图

所以问题就是已知左右各有一堆点,左右之间还有一些边,怎样才能选取最多的边使得被连接的点最多。

增广路径:一条长度为奇数的路径,由匹配边和非匹配边交替组成,两端的边为非匹配边

那么我们的目的就是寻找尽可能多的增广路径。

如图
【二分图匹配】完美的牛栏_第1张图片

这样就可以使更多的点得到匹配,那么怎么使增广路径尽可能多呢?介绍一下匈牙利算法

//result[i]是i点的匹配点
//used[i]表示i点是否在增广路中
bool hungary(int x)//对于点x寻找增广路
{
    int i;
    for (i=1;i<=n;i++)
        if (map[x][i]&&!used[i])//如果有边
        {
            used[i]=1;
            if (!result[i]||hungary(result[i]))
            //如果没有匹配到边或者匹配到的点的那一头有增广
            {
                result[i]=x;
                return true;
            }
        }
    return false;
}

学习匈牙利的时候傻傻的干过一件事,就是把左边的点和右边的点分开储存,就是比如左边1点和右边2点连线,我会存成map[1][2+1000]=1,后来才明白为什么可以避免左边2点和右边1点连线,这里先卖一个关子,思考一下,不懂的读者后面有答案。

#include 
#include 
#include 
using namespace std;
const int N=201;
int n,m,ans;   bool map[N][N];
bool used[N];  int result[N];
void read()
{
    int i,j,t,temp;
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;i++)
    {
        scanf("%d",&t);
        for (j=1;j<=t;j++)
        {
            scanf("%d",&temp);
            map[i][temp]=1;
        }
    }
    return;
}
bool hungary(int x)
{
    int i;
    for (i=1;i<=n;i++)
        if (map[x][i])
        {
            if (used[i])
                continue;
            used[i]=1;
            if (!result[i]||hungary(result[i]))
            {
                result[i]=x;
                return true;
            }
        }
    return false;
}
void work()
{
    int i;
    for (i=1;i<=n;i++)
    {
        memset(used,0,sizeof(used));
        if (hungary(i))
            ans++;
    }
    printf("%d\n",ans);
    return;
}
int main()
{
    read();
    work();
    return 0;
}

答案在这里!!
就是因为这是一个二分图,所以在俩边的点集内部是肯定没有边的,故可以直接存储。

你可能感兴趣的:(图论,二分图)