题意:
有N头牛,F种食物可以制作,D种饮料可以制作 然后每行代表一头牛的喜好,开头两个数fi,di表示这头牛喜欢fi种食物,di种饮料,接下来fi个数表示喜欢的食物编号,di个数表示喜欢的饮料的编号
现在主人使用最优决策制作出F种食物和D种饮料,问怎么喂才能使尽可能多的牛喂饱(喂饱=一份食物一份饮料,且一头牛最多消耗一份食物和一份饮料) , 输出最多喂饱的牛数
思路:
本题关键是建图,图建完以后就是裸 dinic
Dinnic算法模板
建图方法:
设1+F+D+2N+1个结点,1表示源点,1+F+D+2N+1表示汇点 *
1+1 ~ 1+F表示食物结点,1+F+1 ~ 1+F+N表示牛的编号,1+F+N+1 ~ 1+F+2N也表示牛的编号,为什么这样后面说明 * 1+F+2N+1 ~ 1+F+2*N+D表示饮料编号 * * 然后被喜欢的食物指向喜欢它的牛的所有边,牛i指向牛i的所有边,牛指向它喜欢的饮料的所有边,源点到所有食物,饮料到所有汇点的边,权值全设为1 * *
建图解释:
首先,这幅图被分为几个模块,从左到右依次为:食物制作模块(src-F边集),喂食物模块(F-N1边集),牛吃食物模块(N1-N2边集),喂饮料模块(N2-D边集)和牛喝饮料模块(D-tar边集) 然后解释为什么设权值为1,
第一:人做的食物和水都是一份;第二:牛最多吃一份食物(水),就算有多份食物指向牛,由于下一条边(牛指出的边,即牛吃食物(喝饮料)模块)为1,意味着最后只能流出1的流量,即吃掉其中一份食物,水的指出同理 ,这样就能想到为什么要设两次牛了,就是要通过牛-牛边控制流量为1,即通过食物选择模块后,保证一头牛只吃了一份食物 , 然后吃完食物流量为1了,就只能选一条路喝饮料,最后饮料流出的也是唯一的一条边,表示这种饮料最终只能被一头牛选择
//#include<bits/stdc++.h>
#include<cstdio>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=1e3+10;
int F,D,N;
int g[maxn][maxn];
int lv[maxn];
//bfs分层图过程
bool bfs(int src,int tar)
{
memset(lv,0,sizeof lv);
queue<int>que;
que.push(src);
lv[src]=1;//源点的深度为1
while(!que.empty()){
int cur=que.front();
que.pop();
for(int i=src;i<=tar;i++)
//若该残量不为0,并未分配深度
if(!lv[i]&&g[cur][i]){
lv[i]=lv[cur]+1;
if(i==tar) return 1;
que.push(i);
}
}
return 0;
}
//寻找增广路
//cur是当前节点,src是源点,tar是汇点,totflow当前流量
int dfs(int cur,int src,int tar,int totflow){
int ret=0;
if(cur==tar||!totflow)//当已经到达汇点,直接返回
return totflow;
for(int i=src;i<=tar;i++){
if(totflow==0) break;//若没有增广路
//满足分层图和残量不为0两个条件
if(g[cur][i]&&lv[cur]+1==lv[i]){
int f=min(totflow,g[cur][i]);
int flowdown=dfs(i,src,tar,f);//向下增广
ret+=flowdown;
totflow-=flowdown;
g[cur][i]-=flowdown;//正向边减
g[i][cur]+=flowdown;//反向边加
}
}
return ret;
}
int dinic(int src,int tar,int num)
{
int ret=0;//记录最大流量
while(bfs(src,tar)){
int tmp=dfs(src,src,tar,num);
if(!tmp) break;
ret+=tmp;
}
return ret;
}
int main()
{
while(scanf("%d%d%d",&N,&F,&D)!=EOF){
int src=1;//源点
int tar=1+N*2+F+D+1;//汇点
for(int i=1;i<=F;i++)//src-F边集
g[src][src+i]=1;
for(int i=1+N*2+F+1;i<tar;i++)//D-tar边集
g[i][tar]=1;
for(int i=1;i<=N;i++){
g[src+F+i][src+F+i+N]=1;//N1-N2边集
int fi,di;
scanf("%d%d",&fi,&di);
int tmp;
while(fi--){//F-N1边集
scanf("%d",&tmp);
g[src+tmp][src+F+i]=1;
}
while(di--){
scanf("%d",&tmp);
g[src+F+i+N][src+F+2*N+tmp]=1;
}
}
printf("%d\n",dinic(src,tar,D+F));
}
return 0;
}