奶牛们在吃饭方面十分挑剔。
每头奶牛都有自己喜欢的食物和饮料,并且不会食用其他不喜欢的食物和饮料。
农夫约翰为他的奶牛们做了美味的饭菜,但他忘了对照他们的喜好来检查菜单。
虽然他可能无法令所有奶牛满意,但他想给尽可能多的奶牛提供一顿完整的用餐----既有食物可吃,也有饮料可喝。
农夫约翰一共烹制了 F 种食物,并提供了 D 种饮料。
约翰共有 N 头奶牛,其中第 i 头奶牛有 Fi 种喜欢的食物以及 Di 种喜欢的饮料。
约翰需要给每头奶牛分配一种食物和一种饮料,并使得有吃有喝的奶牛数量尽可能大。
每种食物或饮料都只有一份,所以只能分配给一头奶牛食用(即,一旦将第 2 种食物分配给了一头奶牛,就不能再分配给其他奶牛了)。
输入格式
第一行包含三个整数 N,F,D。
接下来 N 行,其中第 i 行描述第 i 头奶牛的饮食喜好,首先包含两个整数 Fi 和 Di,表示其喜欢的食物和饮料数量,然后包含 Fi 个整数表示其喜欢的食物的种类编号,最后包含 Di 个整数表示其喜欢的饮料的种类编号。
食物编号从 1 到 F,饮料编号从 1 到 D。
输出格式
输出一个整数,表示能够有吃有喝的奶牛的最大数量。
数据范围
1≤N,F,D≤100,
1≤Fi≤F,
1≤Di≤D
输入样例:
4 3 3
2 2 1 2 3 1
2 2 2 3 1 2
2 2 1 3 1 2
2 1 1 3 3
输出样例:
3
样例解释
一种使得三头奶牛满意的可行方法是:
奶牛 1:没饭。
奶牛 2:食物 2,饮料 2。
奶牛 3:食物 1,饮料 1。
奶牛 4:食物 3,饮料 3。
题解
当一个点只能被选择一次的时候,可以使用最大流中的拆点思路。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 410, M = 40610, INF = 1e8;
int n, F, D, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c)
{
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (d[ver] == -1 && f[i])
{
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit)
{
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i])
{
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int r = 0, flow;
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main()
{
scanf("%d%d%d", &n, &F, &D);
S = 0, T = n * 2 + F + D + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= F; i ++ ) add(S, n * 2 + i, 1);
for (int i = 1; i <= D; i ++ ) add(n * 2 + F + i, T, 1);
for (int i = 1; i <= n; i ++ )
{
add(i, n + i, 1);
int a, b, t;
scanf("%d%d", &a, &b);
while (a -- )
{
scanf("%d", &t);
add(n * 2 + t, i, 1);
}
while (b -- )
{
scanf("%d", &t);
add(i + n, n * 2 + F + t, 1);
}
}
printf("%d\n", dinic());
return 0;
}