Description
Farmer John completed his new barn just last week, complete with all the latest milking technology. Unfortunately, due to engineering problems, all the stalls in the new barn are different. For the first week, Farmer John randomly assigned cows to stalls, but it quickly became clear that any given cow was only willing to produce milk in certain stalls. For the last week, Farmer John has been collecting data on which cows are willing to produce milk in which stalls. A stall may be only assigned to one cow, and, of course, a cow may be only assigned to one stall.
Given the preferences of the cows, compute the maximum number of milk-producing assignments of cows to stalls that is possible.
Input
The input includes several cases. For each case, the first line contains two integers, N (0 <= N <= 200) and M (0 <= M <= 200). N is the number of cows that Farmer John has and M is the number of stalls in the new barn. Each of the following N lines corresponds to a single cow. The first integer (Si) on the line is the number of stalls that the cow is willing to produce milk in (0 <= Si <= M). The subsequent Si integers on that line are the stalls in which that cow is willing to produce milk. The stall numbers will be integers in the range (1..M), and no stall will be listed twice for a given cow.
Output
For each case, output a single line with a single integer, the maximum number of milk-producing stall assignments that can be made.
Sample Input
5 5
2 2 5
3 2 3 4
2 1 5
3 1 2 5
1 2
Sample Output
4
Source
先介绍一些二分图的基本概念:
二分图
二分图是图论中的一种特殊模型。
设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i∈A,j∈B),则称图G为一个二分图。
如图就是一个二分图。
二分图的匹配
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
求二分图最大匹配可以用最大流或者匈牙利算法。
最大匹配
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
匈牙利算法
求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的.但是这个算法的复杂度为边数的指数级函数.因此,需要寻求一种更加高效的算法。
增广路的定义(也称增广轨或交错轨):
若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。
由增广路的定义可以推出下述三个结论:
1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M.。
2-P经过取反操作可以得到一个更大的匹配M'。
3-M为G的最大匹配当且仅当不存在相对于M的增广路径。
引用Matrix67大牛blog上的一句话概括下求二分图最大匹配的匈牙利算法:从二分图中找出一条路径来,让路径的起点和终点都是还没有匹配过的点,并且路径经过的连线是一条没被匹配、一条已经匹配过,再下一条又没匹配这样交替地出现。找到这样的路径后,显然路径里没被匹配的连线比已经匹配了的连线多一条,于是修改匹配图,把路径里所有匹配过的连线去掉匹配关系,把没有匹配的连线变成匹配的,这样匹配数就比原来多1个。不断执行上述操作,直到找不到这样的路径为止。
从上面这段话,可以构造出匈牙利算法的算法轮廓:
bool 寻找从k出发的对应项出的可增广路{
while(j与k邻接){
if(j不在增广路上){
把j加入增广路;
if(j是未盖点 或者 从j的对应项出发有可增广路)
则从k的对应项出有可增广路,返回true;
修改j的对应项为k;
}
}
从k的对应项出没有可增广路,返回false;
}
void 匈牙利hungary(){
for i->1 to n{
if(则从i的对应项出有可增广路)
匹配数++;
}
输出 匹配数;
}
然后引入几个数据结构:adj为图的邻接表,visit[i]记录点i是否被扫描(匹配)过,match[i]存储了匹配的方案(点集Y中的点i匹配X中的match[i],初始值为-1)。
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 201;
bool visit[MAXN];
int n,m,mark[MAXN];
vector< vector<int> > adj;
bool dfs(int pos){
int i,j,pre,len=adj[pos].size();
for(i=0;i<len;i++){
j=adj[pos][i];
if(!visit[j]){
visit[j]=true,pre=mark[j],mark[j]=pos;
if(pre==-1 || dfs(pre))
return true;
mark[j]=pre;
}
}
return false;
}
int hungary(){
int i,ans=0;
for(i=1;i<=n;i++){
memset(visit,false,sizeof(visit));
if(dfs(i)) ans++;
}
return ans;
}
int main(){
int i,j,t;
while(scanf("%d %d",&n,&m)!=EOF){
memset(mark,-1,sizeof(mark));
adj.assign(n+1,vector<int>());
for(i=1;i<=n;i++){
scanf("%d",&t);
while(t--){
scanf("%d",&j);
adj[i].push_back(j);
}
}
printf("%d\n",hungary());
}
return 0;
}