题目链接: POJ - 3281
题目大意
n头牛, f种食物, d种饮料, 每头牛有若干种喜欢的食物和若干种喜欢的饮料, 每种食物或饮料只能使用一次, 求最多能让多少头牛同时吃上喜欢的一个食物和一个饮料
思路
建图, 最大流
建图方法
- 将每种食物f拆成两个节点f1, f2, 从f1向f2连一条容量为1的边, 以保证每个食物只能用一次
- 饮料同样做拆分处理成d1, d2
- 每个牛c也拆分成c1, c2, 从c1向c2练一条容量为1的边, 以保证最后得到的最大流是一个食物-一个牛-一个饮料这样的路径组成的(保证最后的结果是同时吃上喜欢的食物和饮料的牛数)
- 如果一头牛c喜欢食物f, 连边f2->c1, 容量为1
- 如果一头牛c喜欢饮料d, 连边c2->d1, 容量为1
- 图中所有食物的第一个节点是源点, 所有饮料的第二个节点是汇点, 设置一个超级源点S和一个超级汇点T, 连接所有S->f1, d2->T, 容量为1
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int MAXV = 1100, INF = 0X3F3F3F3F;
struct edge
{
int to, cap, rev;
edge(int a, int b, int c) :to(a), cap(b), rev(c){}
};
vector<edge> G[MAXV];
int iter[MAXV], level[MAXV], S, T;
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));
}
bool bfs()
{
memset(level, -1, sizeof(level));
level[S] = 0;
queue<int> que;
que.push(S);
while(!que.empty())
{
int v = que.front(); que.pop();
for(int i=0; i<(int)G[v].size(); ++i)
{
edge &e = G[v][i];
if(e.cap>0 && level[e.to]==-1)
{
level[e.to] = level[v] + 1;
que.push(e.to);
}
}
}
return level[T]!=-1;
}
int dfs(int v, int f)
{
if(v == T) return f;
for(int &i=iter[v]; i<(int)G[v].size(); ++i)
{
edge &e = G[v][i];
if(e.cap>0 && level[e.to]>level[v])
{
int d = dfs(e.to, min(f, e.cap));
if(d)
{
e.cap -= d;
G[e.to][e.rev].cap+= d;
return d;
}
}
}
return 0;
}
int max_flow()
{
int flow = 0;
while(bfs())
{
memset(iter, 0, sizeof(iter));
int f;
while((f=dfs(S, INF))) flow += f;
}
return flow;
}
int n, f, d, nf, nd, x;
int main()
{
while(scanf("%d%d%d", &n, &f, &d) == 3)
{
S = 0;
int sum = n + f + d;
T = 2*sum+1;
for(int i=1; i<=sum; ++i) add_edge(i, i+sum, 1);//将牛, 食物, 饮料拆分成两个节点并连边
for(int i=1; i<=n; ++i)
{
scanf("%d%d", &nf, &nd);
for(int j=0; j<nf; ++j)
{
scanf("%d", &x);
add_edge(sum+n+x, i, 1);//食物和牛
}
for(int j=0; j<nd; ++j)
{
scanf("%d", &x);
add_edge(sum+i, n+f+x, 1);//牛和饮料
}
}
for(int i=n+1; i<=n+f; ++i) add_edge(S, i, 1);//超级源点
for(int i=sum+n+f+1; i<T; ++i) add_edge(i, T, 1);//超级汇点
printf("%d\n", max_flow());
for(int i=0; i<MAXV; ++i) G[i].clear();
}
return 0;
}