题外话 牛这个东西 除了usaco之外 还有出题人会用吗。。。
【题目描述】
农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术。不幸的是,由于工程问题,每个牛栏都不一样。第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在她们喜欢的那些牛栏中产奶。上个星期,农夫约翰刚刚收集到了奶牛们的爱好的信息(每头奶牛喜欢在哪些牛栏产奶)。一个牛栏只能容纳一头奶牛,当然,一头奶牛只能在一个牛栏中产奶。
给出奶牛们的爱好的信息,计算最大分配方案。
【问题分析】
很简单的二分图匹配 那么就在这里讲一下什么叫做二分图匹配
二分图:如果一张无向图可以被分成两个点集,使得点集内部的点之间没有边,则称这张图是一个二分图
所以问题就是已知左右各有一堆点,左右之间还有一些边,怎样才能选取最多的边使得被连接的点最多。
增广路径:一条长度为奇数的路径,由匹配边和非匹配边交替组成,两端的边为非匹配边
那么我们的目的就是寻找尽可能多的增广路径。
如图
这样就可以使更多的点得到匹配,那么怎么使增广路径尽可能多呢?介绍一下匈牙利算法
//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 <iostream>
#include <cstdio>
#include <cstring>
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;
}
答案在这里!!
就是因为这是一个二分图,所以在俩边的点集内部是肯定没有边的,故可以直接存储。