poj3281 网络流

Description

Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.

Farmer John has cooked fabulous meals for his cows, but he forgot to check his menu against their preferences. Although he might not be able to stuff everybody, he wants to give a complete meal of both food and drink to as many cows as possible.

Farmer John has cooked F (1 ≤ F ≤ 100) types of foods and prepared D (1 ≤ D ≤ 100) types of drinks. Each of his N (1 ≤ N ≤ 100) cows has decided whether she is willing to eat a particular food or drink a particular drink. Farmer John must assign a food type and a drink type to each cow to maximize the number of cows who get both.

Each dish or drink can only be consumed by one cow (i.e., once food type 2 is assigned to a cow, no other cow can be assigned food type 2).

Input

Line 1: Three space-separated integers: NF, and D 
Lines 2..N+1: Each line i starts with a two integers Fi and Di, the number of dishes that cow i likes and the number of drinks that cow i likes. The next Fi integers denote the dishes that cow i will eat, and the Di integers following that denote the drinks that cow i will drink.

Output

Line 1: A single integer that is the maximum number of cows that can be fed both food and drink that conform to their wishes

Sample Input

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

Sample Output

3

题意:
 

题目大意:有n头牛,f种草料,d种水,每种草料和每种水仅有一个单位,每头牛要吃一个单位草料和喝一个单位水。每头牛只对一部分草料和一部分水感兴趣。问你最多能完全满足多少头牛(草料和水均满意)?

分析:经典构图题:用拆点来限制流量,超级源点与每种食物相连,负载为1。每头牛拆点,负载为1(此处限制流量,一头牛只能转移一的流量)。每头牛拆点的出点与所有满意的食物相连,负载为1;每头牛拆点的入点再与所有满意的水相连,负载为1,每种水再与超级汇点相连,负载为1。最终看最大流跑出多少就是最大的,能够双双满足的牛的数量。

 有人可能会说为什么牛要分成两个点,下面解释一下。

 建图解释:
 * 首先,这幅图被分为几个模块,从左到右依次为:食物制作模块(src-F边集),喂食物模块(F-N1边集),牛吃食物模块(N1-N2边集),喂饮料模块(N2-D边集)和牛喝饮料模块(D-tar边集)
 * 然后解释为什么设权值为1,第一:人做的食物和水都是一份;第二:牛最多吃一份食物(水),就算有多份食物指向牛,由于下一条边(牛指出的边,即牛吃食物(喝饮料)模块)为1,意味着最后只能流出1的流量,即吃掉其中一份食物,水的指出同理
 * 这样就能想到为什么要设两次牛了,就是要通过牛-牛边控制流量为1,即通过食物选择模块后,保证一头牛只吃了一份食物
 * 然后吃完食物流量为1了,就只能选一条路喝饮料,最后饮料流出的也是唯一一条边,表示这种饮料最终只能被一头牛选择

 例如上面这张图,不把牛分成两个点,这样不能保证每头牛只喝一个饮料和只吃一种食物。

代码:
 

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define INF 1e9
using namespace std;
const int maxn=500+10;
 
struct Edge
{
    int from,to,cap,flow;
    Edge(){}
    Edge(int f,int t,int c,int fl):from(f),to(t),cap(c),flow(fl){}
};
 
struct Dinic
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int d[maxn];
    bool vis[maxn];
    int cur[maxn];
 
    void init(int n,int s,int t)
    {
        this->n=n,this->s=s,this->t=t;
        edges.clear();
        for(int i=0;i<n;i++) G[i].clear();
    }
 
    void AddEgde(int from,int to,int cap)
    {
        edges.push_back( Edge(from,to,cap,0) );
        edges.push_back( Edge(to,from,0,0) );
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
 
    bool BFS()
    {
        queue<int> Q;
        memset(vis,0,sizeof(vis));
        vis[s]=true;
        d[s]=0;
        Q.push(s);
        while(!Q.empty())
        {
            int x=Q.front(); Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge& e=edges[G[x][i]];
                if(!vis[e.to] && e.cap>e.flow)
                {
                    vis[e.to]=true;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
 
    int DFS(int x,int a)
    {
        if(x==t || a==0) return a;
        int flow=0,f;
        for(int& i=cur[x];i<G[x].size();i++)
        {
            Edge& e=edges[G[x][i]];
            if(d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow) ) ) >0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow -=f;
                flow+=f;
                a-=f;
                if(a==0) break;
            }
        }
        return flow;
    }
 
    int Max_flow()
    {
        int ans=0;
        while(BFS())
        {
            memset(cur,0,sizeof(cur));
            ans +=DFS(s,INF);
        }
        return ans;
    }
}DC;
 
int main()
{
    int N,F,D;
    while(scanf("%d%d%d",&N,&F,&D)==3)
    {
        int src=0,dst=1+2*N+F+D;
        DC.init(2+2*N+F+D,src,dst);
        for(int i=1;i<=F;i++) DC.AddEgde(src,i+2*N,1);
        for(int i=1;i<=D;i++) DC.AddEgde(i+2*N+F,dst,1);
        for(int i=1;i<=N;i++)
        {
            DC.AddEgde(i,i+N,1);
            int food_num,drink_num;
            scanf("%d%d",&food_num,&drink_num);
            while(food_num--)
            {
                int f;
                scanf("%d",&f);
                DC.AddEgde(f+2*N,i,1);
            }
            while(drink_num--)
            {
                int d;
                scanf("%d",&d);
                DC.AddEgde(i+N,d+2*N+F,1);
            }
        }
        printf("%d\n",DC.Max_flow());
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值