USACO 奶牛食品(最大流)

题目描述
FJ的奶牛们只吃各自喜欢的一些特定的食物和饮料,除此之外的其他食物和饮料一概不吃。某天FJ为奶牛们精心准备了一顿美妙的饭食,但在之前忘记检查奶牛们的菜单,这样显然是不能不能满足所有奶牛的要求。但是FJ又不愿意为此重新来做,所以他他还是想让尽可能多的牛吃到他们喜欢的食品和饮料。
FJ提供了F (编号为1、2、…、F)种食品并准备了D (编号为1、2、…、D)种饮料, 他的N头牛(编号为1、2、…、N)都已决定了是否愿意吃某种食物和喝某种饮料。FJ想给每一头牛一种食品和一种饮料,使得尽可能多的牛得到喜欢的食物和饮料。
每一种食物和饮料只能由一头牛来用。例如如果食物2被一头牛吃掉了,没有别的牛能吃到食物2。
输入
第一行包含三个用空格分开的整数N,F和D;1<=F<=100,1<=D<=100,1<=N<=100

接下来的N行描述每个奶牛的信息:第i+1行的前两个整数为F_i和D_i,接下来的F_i个整数表示奶牛i喜欢的食品编号,再接下来的D_i个整数表示奶牛i喜欢的饮料编号。

输出
仅一行一个整数,表示FJ最多能让多少头奶牛吃到自己喜欢的食品和饮料。

样例输入
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
样例输出
3
提示
输入数据表明:奶牛1喜欢的食物1、2;喜欢喝饮料3、1;奶牛2喜欢的食物2、3;喜欢喝饮料1、2;奶牛3喜欢的食物1、3;喜欢喝饮料1、2;奶牛4喜欢的食物1、3;喜欢喝饮料3;

那么下面的分配方法将是最优的:奶牛1不给食品和饮料;奶牛2分配食物2和饮料2;奶牛3分配食物1和饮料2;奶牛4分配食物3和饮料4。

想一想就能发现,这是一道简单的网络流的题目。同学戏称为“三分图匹配”[手动笑哭]做这道题的时候我们只需要把奶牛放在中间,把每个奶牛拆成两头,这两头之间的容量为1,以保证一头牛就连一个饮料和食品,把饮料和食品放在两旁再加上源点和汇点就可以开始做最大流了。


#include<cstdio>
#define MAXN 410
#define min(a,b) (a)<(b)?(a):(b)
#define INF 0x3f3f3f3f
int n, ans, F, D, s, t;
int c[MAXN][MAXN];
int d[MAXN], vd[MAXN];
int aug(int u, int augco)
{
    int augc = augco, delta, dmin = t;
    if(u == t) return augc;
    for(int v = s; v <= t; v ++)
        if(c[u][v])
        {
            if(d[u] == d[v] + 1)
            {
                delta = min(augc, c[u][v]);
                delta = aug(v, delta);
                c[u][v] -= delta;
                c[v][u] += delta;
                augc -= delta;
                if(d[s] > t) return augco - augc;
                if(augc == 0) break;
            }
            dmin = min(dmin, d[v]);
        }
    if(augco == augc)
    {
        -- vd[d[u]];
        if(!vd[d[u]])
            d[s] = t + 1;
        d[u] = dmin + 1;
        ++ vd[d[u]];
    }
    return augco - augc;
}
void sap()
{
    vd[0] = t + 1;
    while(d[s] < t)
        ans += aug(s,INF);
}
int main()
{
    int t1, t2, t3;
    scanf("%d%d%d", &n, &F, &D);
    t = 2*n + F + D + 1;
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d%d", &t1, &t2);
        for(int j = 1; j <= t1; j ++)
        {
            scanf("%d",&t3);
            c[t3][F+i*2-1] = 1;
        }
        for(int j = 1; j <= t2; j ++)
        {
            scanf("%d",&t3);
            c[F+i*2][F+2*n+t3] = 1;
        }
    }
    for(int i = 1; i <= F; i ++) c[s][i] = 1;
    for(int i = 1; i <= D; i ++) c[i+2*n+F][t] = 1;
    for(int j = 1; j <= n; j ++) c[F+j*2-1][F+j*2] = 1;
    sap();
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值