题意大概是:一个大厨,有N头牛,每头牛都会有自己喜欢吃的饭菜和喜欢喝的饮料。现在大厨做了F份饭餐,D份饮料,只有同时有吃有喝的牛才会开心。然后问,最多有几头牛可以开心。
这道题的难度在于建图,还有想到拆点这一点。
先说怎么建图吧。
我们把牛放在中间,左边食物,右边饮品,进行连线。然后源点和汇点怎么找呢? 我们让源点与所有的是食物有一条边,让汇点与所有的饮品有一条边。这样子找最大流的时候,就能保证是 源点—— 食物 —— 牛 —— 牛 —— 饮品 —— 汇点。而所有的边的权值都是1 ,这样我们找到的最大流就一定是一只牛配上一种食物和一个饮品。
而至于为要拆点,其实也很显而易见。如果不拆点,有可能两队食物和饮品同时连在同一头牛身上,这就不符合题意了。
然后难点就是读图,算法的板子就是那个样子。 我们把图读好就没有问题了。。
typedef struct Edge{
int u, v, w, next;
}edge;
edge e[len];
int head[len];
int cur[len];
int depth[len];
int S, T, N, F, D, hcnt;
int main(){
int Fi, Di, a, b;
while (cin >> N >> F >> D){
Init();
for(int i=1; i<=F; i++) adde(0, i, 1);//源点到食物
for(int i=1; i<=N; i++){
cin >> Fi >> Di;
for(int j=1; j<=Fi; j++){
cin >> a;
adde(a, F+i, 1);//食物 到 牛
}
for(int j=1; j<=Di; j++){
cin >> b;
adde(F+N+i, F+2*N+b, 1);//牛到饮品
}
}
for (int i=1; i<=N; i++) adde(F+i, F+N+i, 1); //牛牛
for (int i=1; i<=D; i++) adde(F+2*N+i, F+2*N+D+1, 1); //饮品到汇点
S = 0; T = F + 2*N + D + 1;
int ans = dinic();
cout << ans << endl;
}
return 0;
}
void Init(){
memset(head, -1, sizeof (head));
S = T = hcnt = 0;
}
void adde(int u, int v, int w){
e[hcnt].u = u; e[hcnt].v = v; e[hcnt].w = w;
e[hcnt].next = head[u]; head[u] = hcnt++;
e[hcnt].u = v; e[hcnt].v = u; e[hcnt].w = 0;
e[hcnt].next = head[v]; head[v] = hcnt++;
}
bool BFS(){
queue <int> q;
for (int i=0; i<=T; i++) depth[i] = -1;
q.push(S); depth[S] = 0;
while (!q.empty()){
int h = q.front(); q.pop();
if (h == T) return true;
for (int i=head[h]; ~i; i = e[i].next){
int v = e[i].v;
if (depth[v] != -1 || e[i].w <= 0) continue;
depth[v] = depth[h] + 1;
q.push(v);
}
}
return false;
}
int DFS(int s, int mw){
if (s == T || mw == 0) return mw;
int flow = 0;
for (int i=cur[s]; ~i; i=e[i].next){
cur[s] = i;
int v = e[i].v;
if (depth[v] == depth[s] + 1 && e[i].w >0){
flow = DFS(v, min(mw, e[i].w));
if (flow > 0){
e[i].w -= flow;
e[i^1].w += flow;
return flow;
}
}
}
return 0;
}
int dinic(){
int ans = 0;
while(BFS()){
for (int i=0; i<2*N+F+D+2; i++) cur[i] = head[i];
int v = DFS(S, INF);
ans += v;
}
return ans;
}