题目链接:http://poj.org/problem?id=3281
题意:
农场有很多牛,不同的牛有着多种不同的食物和饮料偏好,每顿饭吃一种食物和饮料,每种饮料和食物只能被一头牛吃,问最多能使多少牛即吃到喜欢食物又喝到喜欢的饮料。
分析:
将牛拆分为2个点,一个跟食物连边,另一个跟饮料连边,然后对应的牛之间进行连边。最后虚拟出一个源点和汇点,计算从源点到汇点的最大流即可。
源点——>食物——>食物牛——>饮料牛——>饮料——>汇点 在图中,1 ~ n 代表食物牛,n + 1 ~ 2n 代表饮料牛,2n + 1 ~ 2n + f 代表食物,2n + f + 1 ~ 2n + f + d 代表饮料。随后的两个点代表源点和汇点。
代码:
#include <vector>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <queue>
#define INF (1 << 29)
#define MAX_V 500
#define fp(PP,QQ,RR) for(int PP = QQ;PP < RR;PP ++)
using namespace std;
struct edge {
int to,cap,rev;
edge(int a = 0,int b = 0,int c = 0):to(a),cap(b),rev(c){}
};
vector <edge> G[MAX_V];
int n,f,d,level[MAX_V],iter[MAX_V];
void add_edge(int from,int to,int cap){
G[from].push_back(edge(to,cap,G[to].size()));
G[to].push_back(edge(from,0,G[from].size() - 1));
}
void bfs(int s){
memset(level,-1,sizeof(level));
queue <int> que;
while(!que.empty()) que.pop();
level[s] = 0;
que.push(s);
while(!que.empty()){
int v = que.front();que.pop();
fp(i,0,G[v].size()){
edge & e = G[v][i];
if(e.cap > 0 && level[e.to] < 0){
level[e.to] = level[v] + 1;
que.push(e.to);
}
}
}
}
int dfs(int v,int t,int fl){
if(v == t) return fl;
for(int & i = iter[v];i < G[v].size();i ++){
edge & e = G[v][i];
if(e.cap > 0 && level[v] < level[e.to]){
int d = dfs(e.to,t,min(f,e.cap));
if(d > 0){
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t){
int flow = 0;
while(1){
bfs(s);
if(level[t] < 0) return flow;
memset(iter,0,sizeof(iter));
int fl;
while((fl = dfs(s,t,INF)) > 0){
flow += fl;
}
}
}
int main(void)
{
cin >> n >> f >> d;
fp(i,0,n){
int nf,nd,ff,dd;
cin >> nf >> nd;
fp(j,0,nf){
cin >> ff;
add_edge(ff + 2 * n,i + 1,1);
}
fp(j,0,nd){
cin >> dd;
add_edge(i + 1 + n,dd + 2 * n + f,1);
}
add_edge(i + 1,i + 1 + n,1);
}
fp(i,0,f){
add_edge(n * 2 + f + d + 1,i + 2 * n + 1,1);
}
fp(i,0,d){
add_edge(i + 2 * n + f + 1,n * 2 + f + d + 2,1);
}
int res = 0;
printf("%d\n",max_flow(2 * n + f + d + 1,2 * n + f + d + 2));
return 0;
}