2240. 餐饮
题目链接
2240. 餐饮
奶牛们在吃饭方面十分挑剔。
每头奶牛都有自己喜欢的食物和饮料,并且不会食用其他不喜欢的食物和饮料。
农夫约翰为他的奶牛们做了美味的饭菜,但他忘了对照他们的喜好来检查菜单。
虽然他可能无法令所有奶牛满意,但他想给尽可能多的奶牛提供一顿完整的用餐----既有食物可吃,也有饮料可喝。
农夫约翰一共烹制了 \(F\) 种食物,并提供了 \(D\) 种饮料。
约翰共有 \(N\) 头奶牛,其中第 \(i\) 头奶牛有 \(F_i\) 种喜欢的食物以及 \(D_i\) 种喜欢的饮料。
约翰需要给每头奶牛分配一种食物和一种饮料,并使得有吃有喝的奶牛数量尽可能大。
每种食物或饮料都只有一份,所以只能分配给一头奶牛食用(即,一旦将第 \(2\) 种食物分配给了一头奶牛,就不能再分配给其他奶牛了)。
输入格式
第一行包含三个整数 \(N,F,D\)。
接下来 \(N\) 行,其中第 \(i\) 行描述第 \(i\) 头奶牛的饮食喜好,首先包含两个整数 \(F_i\) 和 \(D_i\),表示其喜欢的食物和饮料数量,然后包含 \(F_i\) 个整数表示其喜欢的食物的种类编号,最后包含 \(D_i\) 个整数表示其喜欢的饮料的种类编号。
食物编号从 \(1\) 到 \(F\),饮料编号从 \(1\) 到 \(D\)。
输出格式
输出一个整数,表示能够有吃有喝的奶牛的最大数量。
数据范围
\(1 \le N,F,D \le 100\),
\(1 \le F_i \le F\),
\(1 \le D_i \le 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\)。
解题思路
最大流,拆点
建图:左边为食物,中间为奶牛,右边为饮料,对于每一奶牛,向左边它喜欢的食物建反向边,向右边它喜欢的饮料建边,容量都为 \(1\),建立源点 \(S\),其连向所有的事物,建立汇点 \(T\),所有的饮料连向 \(T\),所有的容量都为 \(1\),但这样会有问题,可能会有好多流量经过同一个奶牛表示的点,可以对这部分的点拆点,即一分为二,食物连向入点,出点连向饮料,入点连向出点,且容量为 \(1\),这样即可保证最多只能有一条流量经过一个奶牛表示的节点,求解的最大流即为答案。\(\color{red}{为什么?}\)不难发现,每条路径不可能经过同一个点,正好对应每种食物和饮料只能使用一次,且经过的中间的点即为使用者
- 时间复杂度:\(O(n^2m)\)
代码
// Problem: 餐饮
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2242/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=105*4,M=(205+105+200*100)*2,inf=1e8;
int n,F,D,S,T;
int h[N],e[M],f[M],ne[M],idx;
int hh,tt,q[N],cur[N],d[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()
{
memset(d,-1,sizeof d);
hh=tt=d[S]=0;
q[0]=S;
cur[S]=h[S];
while(hh<=tt)
{
int x=q[hh++];
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(d[y]==-1&&f[i])
{
d[y]=d[x]+1;
cur[y]=h[y];
if(y==T)return true;
q[++tt]=y;
}
}
}
return false;
}
int dfs(int x,int limit)
{
if(x==T)return limit;
int flow=0;
for(int i=cur[x];~i&&flow<limit;i=ne[i])
{
cur[x]=i;
int y=e[i];
if(d[y]==d[x]+1&&f[i])
{
int t=dfs(y,min(f[i],limit-flow));
if(!t)d[y]=-1;
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
int dinic()
{
int res=0,flow;
while(bfs())while(flow=dfs(S,inf))res+=flow;
return res;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d%d",&n,&F,&D);
S=0,T=2*n+F+D+1;
for(int i=1;i<=F;i++)add(S,2*n+i,1);
for(int i=1;i<=D;i++)add(2*n+F+i,T,1);
for(int i=1;i<=n;i++)
{
int f,d;
scanf("%d%d",&f,&d);
add(i,n+i,1);
int x;
while(f--)
{
scanf("%d",&x);
add(2*n+x,i,1);
}
while(d--)
{
scanf("%d",&x);
add(n+i,2*n+F+x,1);
}
}
printf("%d",dinic());
return 0;
}