这道题真的很难想到是用最大流做得,主要是建图,大概就是这样:
然后每条边得容量为1.
然后牛和牛之间好像必须有对应得一条边,因为如果不这样的话,比如我们寻求增广路的时候,第一次选了f1、1号牛、d1,然后继续寻找可能就会找到f1、1号牛、d2。而如果加个牛和牛之间的边,恰好解决了这个问题,构思非常妙。下面是用ff板子做的:
import java.util.*;
public class Main {
static int map[][]; //建图
static int pre[]; //储存增广路径
static boolean used[]; //是否用过
static int s,t; //源点和汇点
static int n; //储存节点个数
static boolean bfs(){ //寻找增广路径
for(int i=1;i<=n;i++){pre[i]=0;used[i]=false;}
used[s]=true;
LinkedList<Integer> q=new LinkedList<Integer>();
q.add(s);
while(!q.isEmpty()){
int cur=q.poll();
if(cur==t)return true;
for(int i=1;i<=n;i++){
if(!used[i]&&map[cur][i]!=0){
pre[i]=cur;
q.offer(i);
used[i]=true;
}
}
}
return false;
}
static int maxPath(){
int ans=0;
while(bfs()){
int min=Integer.MAX_VALUE;
for(int i=t;i!=s;i=pre[i])
min=Math.min(min,map[pre[i]][i]);
ans+=min;
for(int i=t;i!=s;i=pre[i]){
map[pre[i]][i]-=min;
map[i][pre[i]]+=min;
}
}
return ans;
}
static void init(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=0;
}
public static void main(String[] args) {
map=new int[1000][1000];
pre=new int[1000];
used=new boolean[1000];
Scanner sc=new Scanner(System.in);
int m=sc.nextInt();
int f=sc.nextInt();
int d=sc.nextInt();
//总点数为2*m+f+d+2,源点为2*m+f+d+1,汇点为2*m+f+d+2
n=m+f+d+2;
s=m+f+d+1;
t=m+f+d+2;
init();
//输入牛和食物,并在其中建边
for(int i=1;i<=m;i++){
int a=sc.nextInt();
int b=sc.nextInt();
while(a--!=0){
int fi=sc.nextInt();
map[fi][f+i]=1;
}
while(b--!=0){
int di=sc.nextInt();
map[f+i][f+m+di]=1;
}
}
//食物和源点建边
for(int i=1;i<=f;i++)
map[s][i]=1;
//饮料和汇点建边
for(int i=1;i<=d;i++)
map[f+m+i][t]=1;
System.out.println(maxPath());
}
}