// [5/28/2014 Sjm]
/*
最大流:多个源点,多个汇点
// 若按二分图匹配做,请参见:http://www.cnblogs.com/shijianming/p/4140846.html
思路:
1)增加一个超级源点 S,从 S 向 每个源点 连一条容量为对应最大流出流量的边
2)增加一个超级汇点 T,从 每一个汇点 向 T 连一条容量为对应最大流入容量的边
对于此题:
源点 cows (范围:M+1 ~ M+N),汇点 stalls (范围:1 ~ M)
设 cows 与 the stalls (in which that cow is willing to produce milk)之间的容量为 1
1)设 0 为超级源点,
因为每个 cow 流出的最大流出流量为 1(只能连一个stall),所以得:超级源点 向每个 cows 的连线的流量为 1
2)设 M+N+1 为超级汇点
因为流入每个 stall 最大流量为 1(只能被一个cow连接),所以得:每个 stall 向 超级汇点 的连线的连线的流量为 1
*/
1 #include <iostream>
2 #include <cstdlib>
3 #include <cstdio>
4 #include <vector>
5 #include <algorithm>
6 using namespace std;
7 const int MAX_V = 410;
8 const int INF = 0x3f3f3f3f;
9 int N, M;
10
11 struct edge { int to, cap, rev; };
12 vector<edge> G[MAX_V];
13 bool used[MAX_V];
14
15 void add_edge(int from, int to, int cap)
16 {
17 edge e1 = { to, cap, G[to].size() };
18 G[from].push_back(e1);
19 edge e2 = { from, 0, G[from].size() - 1 };
20 G[to].push_back(e2);
21 }
22
23 int dfs(int v, int t, int f)
24 {
25 if (v == t) return f;
26 used[v] = true;
27 for (int i = 0; i < G[v].size(); i++) {
28 edge &e = G[v][i];
29 if (!used[e.to] && (e.cap > 0)) {
30 int d = dfs(e.to, t, min(f, e.cap));
31 if (d > 0) {
32 e.cap -= d;
33 G[e.to][e.rev].cap += d;
34 return d;
35 }
36 }
37 }
38 return 0;
39 }
40
41 int Solve(int s, int t)
42 {
43 int flow = 0;
44 for (;;)
45 {
46 memset(used, 0, sizeof(used));
47 int f = dfs(s, t, INF);
48 if (!f) return flow;
49 flow += f;
50 }
51 }
52
53 int main()
54 {
55 //freopen("input.txt", "r", stdin);
56 //freopen("output.txt", "w", stdout);
57 while (~scanf("%d %d", &N, &M))
58 {
59 for (int i = 1; i <= N; i++) {
60 int mycount;
61 scanf("%d", &mycount);
62 int tep;
63 for (int j = 0; j < mycount; j++) {
64 scanf("%d", &tep);
65 add_edge(M + i, tep, 1);
66 }
67 }
68 for (int i = 1; i <= N; i++) {
69 add_edge(0, M+i, 1);
70 }
71 for (int i = 1; i <= M; i++) {
72 add_edge(i, M + N + 1, 1);
73 }
74 printf("%d
", Solve(0, M + N + 1));
75 for (int i = 0; i < MAX_V; i++) {
76 G[i].clear();
77 }
78 }
79 return 0;
80 }