Problem
poj.org/problem?id=3281
vjudge.net/contest/68128#problem/B
Reference
Meaning
有 n 只牛、f 种食物、d 种饮料,每只牛都有喜欢的食物和饮料。现在要给尽量多的牛配一种它喜欢的食物和一种喜欢的饮料,问最多能满足多少只牛。
Analysis
最大流。把饮料和食物分隔在牛的两边,即从饮料到牛连边、从牛到食物连边。因为要保证每只牛都最多只拿到一种食物、一种饮料,即容量限制在牛的点上,就考虑把每只牛拆成两个点,中间连一条容量为 1 的边。
最终建图形如:
超级源点 -> 饮料 -> 牛(入)-> 牛(出)-> 食物 -> 超级汇点
曾经想的是另一种建图:源点连所有牛,容量为 2;每只牛向它喜欢的饮料、食物连容量为 1 的边;所有饮料、食物向汇点连容量为 1 的边。然而失败了。说不上来哪里错了,但又好像是有哪里不对。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define Cow(x) (x + d)
#define Drink(x) (x)
#define Food(x) (x + d + 2 * n)
const int N = 100, F = N, D = N;
const int VERTEX = 2 * N + F + D + 2;
const int EDGE = D + N + F + N * (D + F);
int head[VERTEX], to[EDGE<<1], nxt[EDGE<<1], cap[EDGE<<1], sz;
void add_edge(int f, int t, int c)
{
to[sz] = t;
cap[sz] = c;
nxt[sz] = head[f];
head[f] = sz++;
}
int dis[VERTEX];
queue<int> que;
int bfs(int s, int t)
{
memset(dis, 0, sizeof dis);
dis[s] = 1;
que.push(s);
for(int tp; !que.empty(); que.pop())
{
tp = que.front();
for(int i = head[tp]; ~i; i = nxt[i])
if(!dis[to[i]] && cap[i] > 0)
{
dis[to[i]] = dis[tp] + 1;
que.push(to[i]);
}
}
return dis[t];
}
int cur[VERTEX];
int dfs(int now, int t, int aug)
{
if(now == t || !aug)
return aug;
int flow = 0, f;
for(int &i = cur[now]; ~i; i = nxt[i])
if(dis[to[i]] == dis[now] + 1 &&
(f = dfs(to[i], t, min(cap[i], aug))) > 0)
{
cap[i] -= f;
cap[i^1] += f;
flow += f;
aug -= f;
if(!aug)
break;
}
return flow;
}
int dinic(int s, int t)
{
int flow = 0;
while(bfs(s, t))
{
for(int i = 0; i <= t; ++i)
cur[i] = head[i];
flow += dfs(s, t, 1);
}
return flow;
}
int main()
{
int n, f, d;
scanf("%d%d%d", &n, &f, &d);
// 源点、汇点
int S = 0, T = 2 * n + f + d + 1;
memset(head, -1, sizeof head);
sz = 0;
// 源点到饮料
for(int did = 1; did <= d; ++did)
{
add_edge(S, Drink(did), 1);
add_edge(Drink(did), S, 0);
}
// 食物到汇点
for(int fid = 1; fid <= f; ++fid)
{
add_edge(Food(fid), T, 1);
add_edge(T, Food(fid), 0);
}
// 牛入到牛出
for(int cid = 1; cid <= n; ++cid)
{
add_edge(Cow(cid), Cow(cid) + n, 1);
add_edge(Cow(cid) + n, Cow(cid), 0);
}
// 饮料到牛入、牛出到食物
for(int c = 1, nf, nd; c <= n; ++c)
{
scanf("%d%d", &nf, &nd);
for(int fid; nf--; )
{
scanf("%d", &fid);
add_edge(Cow(c) + n, Food(fid), 1);
add_edge(Food(fid), Cow(c) + n, 0);
}
for(int did; nd--; )
{
scanf("%d", &did);
add_edge(Drink(did), Cow(c), 1);
add_edge(Cow(c), Drink(did), 0);
}
}
printf("%d\n", dinic(S, T));
return 0;
}