bzoj1711 [Usaco2007 Open]Dingin吃饭 poj3281 Dining

题面

Description
农夫JOHN为牛们做了很好的食品,但是牛吃饭很挑食. 每一头牛只喜欢吃一些食品和饮料而别的一概不吃.虽然他不一定能把所有牛喂饱,他还是想让尽可能多的牛吃到他们喜欢的食品和饮料. 农夫JOHN做了 F(1<=F<=100) F ( 1 <= F <= 100 ) 种食品并准备了 D(1<=D<=100) D ( 1 <= D <= 100 ) 种饮料. 他的 N(1<=N<=100) N ( 1 <= N <= 100 ) 头牛都以决定了是否愿意吃某种食物和喝某种饮料. 农夫JOHN想给每一头牛一种食品和一种饮料,使得尽可能多的牛得到喜欢的食物和饮料. 每一件食物和饮料只能由一头牛来用. 例如如果食物2被一头牛吃掉了,没有别的牛能吃食物2.
Input
1. 第一行: 三个数: N,F,D N , F , 和 D
2. 第2..N+1行: 每一行由两个数开始 Fi F i Di, D i , 分别是第i 头牛可以吃的食品数和可以喝的饮料数.下 Fi F i 个整数是第i头牛可以吃的食品号,再下面的 Di D i 个整数是第i头牛可以喝的饮料号码.
Output
3. 第一行: 一个整数,最多可以喂饱的牛数.
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
输入解释:
牛 1: 食品从 {1,2}, 饮料从 {1,2} 中选
牛 2: 食品从 {2,3}, 饮料从 {1,2} 中选
牛 3: 食品从 {1,3}, 饮料从 {1,2} 中选
牛 4: 食品从 {1,3}, 饮料从 {3} 中选
Sample Output
3
输出解释:
一个方案是:
Cow 1: 不吃
Cow 2: 食品 #2, 饮料 #2
Cow 3: 食品 #1, 饮料 #1
Cow 4: 食品 #3, 饮料 #3
用鸽笼定理可以推出没有更好的解 (一共只有3总食品和饮料).当然,别的数据会更难.

题解

网络流题目的关键就是建模,建模的关键就是根据题目的限制性条件,建立相关的边。
题中的限制性条件有:
1. 每头奶牛只能占用一份食物
2. 每份食物只能被一头奶牛吃
3. 一份食物由一份饮料和一份食品组成

所以我们根据每一份饮料、每一份食物、每一头奶牛建立相关的点。
然后建立一个超级源点,向每一份食物连容量为1的边;建立一个超级汇点,从每份饮料出发连一条容量为1的边;再根据奶牛的喜好,从食物向奶牛连容量为1边、从奶牛向饮料连容量为1的边。
一个意料之外的问题。看似已经建好图了。但是我们发现。这样有可能存在一只奶牛占用多份食物的可能性,所以我们要把所有奶牛拆成两个点。食物指向一个奶牛点,这个奶牛点再指向另外一个奶牛点,再由另外的奶牛点指向饮料。

code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int num=0;char c=' ';bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
        if(c=='-')
            flag=false;
    for(;c>='0'&&c<='9';num=(num<<3)+(num<<1)+c-48,c=getchar());
    return flag ? num : -num;
}
namespace graph{
    const int maxn=500;
    struct node{
        int y,val,next;
    }a[maxn*maxn];
    int head[maxn],top;
    void insert(int x,int y,int v){
        a[top].y=y;
        a[top].val=v;
        a[top].next=head[x];
        head[x]=top++;
    }
    int n,f,d,s,t;
    //1~f食物
    //f+1~f+n奶牛1
    //f+n+1~f+2n奶牛2 
    //f+2n+1~f+2n+d食物 
    //0源点 f+2n+d+1汇点 
    void init(){
        memset(head,-1,sizeof head);
        n=read();f=read();d=read();
        s=0;
        t=2*n+f+d+1;
        for(int i=1;i<=n;i++){
            int F=read();
            int D=read();
            while(F--){
                int x=read();
                insert(x,f+i,1);
                insert(f+i,x,0);
            }
            while(D--){
                int x=read();
                insert(f+i+n,f+2*n+x,1);
                insert(f+2*n+x,f+i+n,0);
            }
        }
        for(int i=1;i<=n;i++){
            insert(f+i,f+i+n,1);
            insert(f+i+n,f+i,0);
        }
        for(int i=1;i<=f;i++){
            insert(0,i,1);
            insert(i,0,0);
        }
        for(int i=1;i<=d;i++){
            insert(f+2*n+i,t,1);
            insert(t,f+2*n+i,0);
        }
    }
}using namespace graph;

namespace MAX_FLOW{
    int vis[maxn],cur[maxn];
    bool bfs(){
        memset(vis,0,sizeof vis);
        queue<int>q;
        q.push(s);vis[s]=1;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i+1;i=a[i].next){
                int v=a[i].y;
                if(vis[v]||a[i].val==0)continue;
                vis[v]=vis[u]+1;
                q.push(v);
                if(v==t)return true;
            }
        }
        return false;
    }
    int dfs(int x,int flow){
        if(x==t)return flow;
        for(int &i=cur[x];i+1;i=a[i].next){
            int y=a[i].y;
            if(vis[y]!=vis[x]+1||a[i].val==0)continue;
            int k=dfs(y,min(flow,a[i].val));
            if(k){
                a[i].val-=k;
                a[i^1].val+=k;
                return k;
            }
        }
        return 0;
    }
}using namespace MAX_FLOW;
int main(){
    init();
    int ans=0;
    const int INF=0x7fffffff;
    while(bfs()){
        for(int i=s;i<=t;i++)
            cur[i]=head[i];
        while(int flow=dfs(0,INF))
            ans+=flow;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值