题目链接: http://poj.org/problem?id=3281
题意: 有n 头牛,f 种食物, d 种饮料。每头牛都有只吃自己喜欢的食物和饮料,吃到了自己想吃的,牛就很满足。(食物和饮料都只有一份, 只能给一头牛吃喝), 求 最多能让多少头牛满足。
输入描述一下, 先输入N,F,D; 然后每一行先输入两个整数 ff, dd。接着跟随 ff 个整数, 表示编号为(此行) i 的牛喜欢的食物编号, 然后是dd 个整数, 是 这头牛 喜欢的饮料的编号。 编号是 1~F (D);
思路: (思路来自大牛, 我只是代码翻译)
将牛分为左牛和右牛,设置超级源点和超级汇点 建图的思路:超级源点->食物->左牛->右牛->饮料->超级汇点 每条边的容量为1 不能直接食物指向牛,牛再指向饮料是因为这样的话可能一头牛就不止分配一种食物和饮料了 画个图来解释
1、若直接食物->牛->饮料
食物 牛 饮料
如图,例如当1到4这条路走过后 4连上5,当2经过4后4又可以连上6,这就造成了一头牛吃了多种食物和饮料
2、食物->左牛->右牛->饮料
这种情况当左牛选中一个食物之后经过右牛,左右牛之间的路径已经满流不能继续走,就避免了一头牛吃多种食物和饮料的情况
此题我用的EK, 目前只会用EK。。。。QAQ
AC代码:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 520;
const int inf = 0x3f3f3f3f;
int cap[maxn][maxn];
int flow[maxn];
int pre[maxn];
int EK_BFS(int s,int n){
memset(pre,-1,sizeof(pre));
flow[s] = inf;
pre[s] = 0;
queue<int> Q;
Q.push(s);
while(!Q.empty()){
int now = Q.front();
Q.pop();
if(now == n) break;
for(int i = 0;i <= n;i ++){
if(i != now && pre[i] == -1 && cap[now][i] > 0){
flow[i] = min(flow[now],cap[now][i]);
pre[i] = now;
Q.push(i);
}
}
}
if(pre[n] != -1)
return flow[n];
return -1;
}
int maxflow(int s,int n){
int sum = 0;
int addflow;
while((addflow = EK_BFS(s,n)) != -1){
int k = n;
while(k != s){
int last = pre[k];
cap[last][k] -= addflow;
cap[k][last] += addflow;
k = last;
}
sum += addflow;
}
return sum;
}
int main()
{ // 1~f f+1~f+n f+n+1~2*n+f 2*n+f+1~2*n+f+d
int n,f,d; //食物 -> 左牛 -> 右牛 -> 饮料
while(~scanf("%d%d%d",&n,&f,&d)){
memset(cap,0,sizeof(cap));
int et = 2 * n + d + f + 1;
int drink = 2 * n + f;
int a,b,c;
for(int i = 1;i <= n;i ++){
scanf("%d%d",&a,&b);
for(int j = 0;j < a;j ++){
scanf("%d",&c);
cap[c][f+i] = 1; //连接食物和左牛
}
for(int j = 0;j < b;j ++){
scanf("%d",&c);
cap[f+n+i][drink+c] = 1; //连接右牛和饮料
}
}
for(int i = 1;i <= f;i ++)
cap[0][i] = 1; //连接超级源点0 与 食物
for(int i = 1;i <= d;i ++)
cap[drink+i][et] = 1; //连接饮料 与 超级汇点 et
for(int i = 1;i <= n;i ++)
cap[f+i][f+n+i] = 1; //连接左牛 和 右牛
printf("%d\n",maxflow(0,et));
}
return 0;
}