[Luogu P2891] [BZOJ 1711] [USACO07OPEN]吃饭Dining

洛谷传送门
BZOJ传送门

题目描述

F F 种食物和D种饮料,每种食物或饮料只能供一头牛享用,且每头牛只享用一种食物和一种饮料。现在有 n n 头牛,每头牛都有自己喜欢的食物种类列表和饮料种类列表,问最多能使几头牛同时享用到自己喜欢的食物和饮料。(1F100,1D100,1n100

输入输出格式

输入格式

第一行三个整数 N,F,D N , F , D

第二行至第 N+1 N + 1 行, 每行若干整数。前两个整数 Fi,Di F i , D i 表示标号为 i i 的牛喜欢的食物、饮料种类。 后面Fi个整数表示其喜欢食物的标号, 接下来 Di D i 个整数表示其喜欢的饮料的标号。

输出格式

一行一个整数 x x , 表示最多有x头牛同时获得自己喜欢的食物和饮料。

输入输出样例

输入样例#1
4 3 3
2 2 1 2 3 1
2 2 2 3 1 2
2 2 1 3 1 2
2 1 1 3 3
输出样例#1
3

解题分析

如果把牛放在一边, 食物、饮料放在另一边, 连边就变得困难起来, 无法保证一头牛同时获得喜欢的食物和饮料。

换个思路, 我们把食物放在左边, 牛放在中间, 饮料放在右边, 之间连容量为 1 1 <script type="math/tex" id="MathJax-Element-13">1</script>的边, 跑一边最大流即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <queue>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 40050
#define S 0
#define T 1000
#define base 100
#define INF 100000000
template <class TT>
IN void in(TT &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int num, f, d, cnt = -1;
int head[1005], layer[1005];
std::queue <int> q;
struct Edge {int to, fl, nex;} edge[MX << 1];
IN void add(R int from, R int to, R int fl)
{edge[++cnt] = {to, fl, head[from]}, head[from] = cnt;}
IN bool BFS()
{
    std::memset(layer, 0, sizeof(layer));
    layer[S] = 1; q.push(S);
    R int now;
    W (!q.empty())
    {
        now = q.front(); q.pop();
        for (R int i = head[now]; ~i; i = edge[i].nex)
        {
            if(edge[i].fl && !layer[edge[i].to])
            layer[edge[i].to] = layer[now] + 1, q.push(edge[i].to);
        }
    }
    return layer[T];
}
int DFS(R int now, R int val)
{
    if(now == T) return val;
    int lef = val, buf;
    for (R int i = head[now]; ~i; i = edge[i].nex)
    {
        if(edge[i].fl && layer[edge[i].to] == layer[now] + 1)
        {
            buf = DFS(edge[i].to, std::min(lef, edge[i].fl));
            if(!buf) continue;
            lef -= buf; edge[i].fl -= buf, edge[i ^ 1].fl += buf;
            if(lef <= 0) return val;
        }
    }
    return val - lef;
}
int Dinic()
{
    int ret = 0;
    W (BFS()) ret += DFS(S, INF);
    return ret;
}
int main(void)
{
    int a, b, c;
    in(num), in(f), in(d);
    std::memset(head, -1, sizeof(head));
    for (R int i = 1; i <= f; ++i) add(S, i, 1), add(i, S, 0);
    for (R int i = 1; i <= num; ++i) add(i + base, i + base * 2, 1), add(i + base * 2, i + base, 0);
    for (R int i = 1; i <= d; ++i) add(i + base * 3, T, 1), add(T, i + base * 3, 0);
    for (R int i = 1; i <= num; ++i)
    {
        in(a), in(b);
        for (R int j = 1; j <= a; ++j)
        in(c), add(c, i + base, 1), add(i + base, c, 0);
        for (R int j = 1; j <= b; ++j)
        in(c), add(i + base * 2, c + base * 3, 1), add(c + base * 3, i + base * 2, 0);
    }
    printf("%d", Dinic());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值