问题描述
牛是很挑食的。每头牛都偏爱特定的食物和饮料,其他的她都不吃。
农夫约翰为他的牛做了美味的饭菜,但他忘了根据它们的喜好检查菜单。虽然他可能不能填饱所有的人,但他想给尽可能多的奶牛提供一顿完整的食物和饮料。
农民John煮了F(1≤F≤100)种食物,准备了D(1≤D≤100)种饮料。他的每头牛(1≤N≤100)已经决定了她是吃某种特定的食物还是喝某种特定的饮料。农民约翰必须为每头奶牛分配一种食物和一种饮料,以使同时获得这两种食物和饮料的奶牛数量最大化。
每一道菜或饮料只能由一头奶牛食用(即,一旦将食物类型2分配给一头奶牛,其他奶牛就不能被分配食物类型2)。
输入
第1行:三个空格分隔的整数:N、F和D
行2 . .N+1:每一行i以两个整数Fi和Di开始,分别是我喜欢的菜的数量和我喜欢的饮料的数量。下一个Fi整数表示我要吃的牛的盘子,后面的Di整数表示我要喝的牛的饮料。
输出
第1行:一个整数,它是可以同时喂养符合奶牛意愿的食物和饮料的最大奶牛数量
分析:
把每个牛拆成两个牛
建立一个
源点->食物->牛->牛->饮料->汇点(两个牛相同)
的图,边权都为1
最大流就是最大匹配数
为什么是 “食物->牛->牛->饮料”而不是“食物->牛->饮料”呢?
因为如果是“食物->牛->饮料”
那么会出现这种情况:
意思是不能保证一头牛只选择一个食物和饮料
而 如果是“食物->牛->牛->饮料”的话:
(画的真烂)
由于有中间黑色箭头指向的那条路的流量限制,所以可以保证只一头牛只选择一个食物和饮料
总图差不多是这样的:
code(EK算法实现):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e3+5;;
int g[maxm][maxm];
int n,f,d;
int mark[maxm];
int pre[maxm];
int all;
int bfs(int s,int t){
memset(mark,0,sizeof mark);
memset(pre,0,sizeof pre);
queue<int>q;
q.push(s);
mark[s]=1;
pre[s]=s;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<=all;i++){
if(!mark[i]&&g[x][i]){
pre[i]=x;
if(i==t)return 1;
mark[i]=1;
q.push(i);
}
}
}
return 0;
}
int ek(int s,int t){
int ans=0;
while(bfs(s,t)){
int d=inf;
for(int i=t;i!=s;i=pre[i]){//找最小detla
d=min(d,g[pre[i]][i]);
}
for(int i=t;i!=s;i=pre[i]){
g[pre[i]][i]-=d;//正边减小
g[i][pre[i]]+=d;//反边增加
}
ans+=d;
}
return ans;
}
int main(){
while(cin>>n>>f>>d){
all=f+n+n+d+1;//总点数
memset(g,0,sizeof g);
for(int i=1;i<=f;i++){//食物是1-f
g[0][i]=1;//源点和食物建边
}
for(int i=1;i<=n;i++){//牛是(f+1)-(f+n) 和(f+n+1)-(f+n+n);
int k1,k2;
cin>>k1>>k2;
while(k1--){
int t;
cin>>t;
g[t][f+i]=1;//食物和牛建边
}
g[f+i][f+n+i]=1;//牛和自己建边
while(k2--){//饮料是 (f+n+n+1)-(f+n+n+d)
int t;
cin>>t;
g[f+n+i][f+n+n+t]=1;//牛和饮料建边
}
}
for(int i=f+n+n+1;i<=f+n+n+d;i++){//和汇点建边
g[i][all]=1;
}
cout<<ek(0,all)<<endl;
}
return 0;
}