#include
<
stdio.h
>
#include
<
string
.h
>
const
int
EDGE_NUM
=
20001
;
//
边数
const
int
POINT_NUM
=
501
;
//
点数
struct
edge
{
int
v;
//
点
int
next;
//
下一边
int
value;
//
当前边流量
}edge[
2
*
EDGE_NUM];
//
边信息,以邻接表形式存储
int
p[POINT_NUM];
//
p[i]记录最后一条以i为起点的边的id,即以i为起点的最后一条边为edge[p[i]],而edge[p[i]].next则为以i为起点的倒数第二条边,以此类推
int
level[POINT_NUM];
//
level[i]记录i点的层次
int
que[POINT_NUM],
out
[POINT_NUM];
//
辅助数组
int
edgeNumber;
void
init()
{
edgeNumber
=
0
;
memset(p,
-
1
,
sizeof
(p));
}
inline
void
addEdge(
int
from,
int
to,
int
value)
//
添加边,以邻接表形式存储
{
edge[edgeNumber].v
=
to;
edge[edgeNumber].value
=
value;
edge[edgeNumber].next
=
p[from];
p[from]
=
edgeNumber
++
;
}
int
Dinic(
int
source,
int
sink,
int
n)
{
int
i,maxFlow
=
0
;
while
(
true
)
{
int
head,tail;
for
(i
=
0
;i
<
n;i
++
)level[i]
=
0
;
level[source]
=
1
;
//
源点为第一层
head
=
0
;tail
=
0
;
que[
0
]
=
source;
//
que这里当队里使用
while
(head
<=
tail)
//
BFS该剩余图,计算每个可达点层次
{
int
cur
=
que[head
++
];
for
(i
=
p[cur];i
!=-
1
;i
=
edge[i].next)
{
if
(edge[i].value
>
0
&&
level[edge[i].v]
==
0
)
{
level[edge[i].v]
=
level[cur]
+
1
;
que[
++
tail]
=
edge[i].v;
}
}
}
if
(level[sink]
==
0
)
break
;
//
不存在增广路
for
(i
=
0
;i
<
n;i
++
)
out
[i]
=
p[i];
//
out[i]动态记录可用边
int
q
=
-
1
;
//
q为已经搜索到的点的个数,que存放途径边信息
while
(
true
)
//
DFS剩余图,查找增广路
{
if
(q
<
0
)
//
当前路为空
{
int
cur
=
out
[source];
for
(;cur
!=-
1
;cur
=
edge[cur].next)
//
查找第一条边
{
if
(edge[cur].value
>
0
&&
out
[edge[cur].v]
!=-
1
&&
level[edge[cur].v]
==
2
)
//
合法第一条边必须满足:1.流量大于0;2.终点有可用边 3:终点层次为2
break
;
}
if
(cur
==-
1
)
break
;
//
找不到第二层,当前剩余图已经没有增广路
que[
++
q]
=
cur;
//
存入第一条边id
out
[source]
=
edge[cur].next;
}
int
curnode
=
edge[que[q]].v;
//
当前路的终点
if
(curnode
==
sink)
//
找到一条增广路
{
int
thisflow
=
edge[que[
0
]].value;
//
thisflow为当前增广路的流量
int
index
=
0
;
//
标记最小流量边的id
for
(i
=
1
;i
<=
q;i
++
)
{
if
(thisflow
>
edge[que[i]].value)
{
thisflow
=
edge[que[i]].value;
index
=
i;
}
}
maxFlow
+=
thisflow;
for
(i
=
0
;i
<=
q;i
++
)
{
edge[que[i]].value
-=
thisflow;
edge[que[i]
^
1
].value
+=
thisflow;
//
与其方向相反的边
}
q
=
index
-
1
;
//
查找下一条增广路时可直接使用当前路的前q条边
}
else
//
尚未找到汇点
{
int
cur
=
out
[curnode];
for
(;cur
!=-
1
;cur
=
edge[cur].next)
{
if
(edge[cur].value
>
0
&&
out
[edge[cur].v]
!=-
1
&&
level[edge[cur].v]
==
level[curnode]
+
1
)
break
;
}
if
(cur
==-
1
)
//
没有下一条路
{
out
[curnode]
=-
1
;
//
标记当前点的可达边为0
q
--
;
}
else
{
que[
++
q]
=
cur;
out
[curnode]
=
edge[cur].next;
//
下一次搜索时可达边从edge[cur].next开始查找
}
}
}
}
return
maxFlow;
}
int
main()
{
int
Nn,Ff,Dd;
while
(scanf(
"
%d%d%d
"
,
&
Nn,
&
Ff,
&
Dd)
!=
EOF)
{
init();
int
foodstart
=
1
;
int
cow1
=
Ff
+
2
;
int
cow2
=
cow1
+
Nn
+
1
;
int
drinkstart
=
cow2
+
Nn
+
1
;
int
end
=
drinkstart
+
Dd
+
1
;
int
i;
for
(i
=
0
;i
<
Nn;i
++
)
//
添加牛边
{
addEdge(cow1
+
i,cow2
+
i,
1
);
addEdge(cow2
+
i,cow1
+
i,
0
);
}
for
(i
=
0
;i
<
Ff;i
++
)
//
添加食物边
{
addEdge(
0
,foodstart
+
i,
1
);
addEdge(foodstart
+
i,
0
,
0
);
}
for
(i
=
0
;i
<
Dd;i
++
)
//
添加饮料
{
addEdge(drinkstart
+
i,end,
1
);
addEdge(end,drinkstart
+
i,
0
);
}
for
(i
=
0
;i
<
Nn;i
++
)
{
int
f,d;
scanf(
"
%d%d
"
,
&
f,
&
d);
int
x;
while
(f
--
)
{
scanf(
"
%d
"
,
&
x);
x
--
;
addEdge(foodstart
+
x,cow1
+
i,
1
);
addEdge(cow1
+
i,foodstart
+
x,
0
);
}
while
(d
--
)
{
scanf(
"
%d
"
,
&
x);
x
--
;
addEdge(cow2
+
i,drinkstart
+
x,
1
);
addEdge(drinkstart
+
x,cow2
+
i,
0
);
}
}
printf(
"
%d\n
"
,Dinic(
0
,end,end
+
1
));
}
return
0
;
}