POJ 3281 Dining 拆点网络流

Dining
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 8304 Accepted: 3835

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

Hint

One way to satisfy three cows is: 
Cow 1: no meal 
Cow 2: Food #2, Drink #2 
Cow 3: Food #1, Drink #1 
Cow 4: Food #3, Drink #3 
The pigeon-hole principle tells us we can do no better since there are only three kinds of food or drink. Other test data sets are more challenging, of course.

Source


题意是说有n头牛,f种食物和d种饮料,每种食物和饮料数量都是1.每头牛都有自己喜欢的食物和饮料,问要怎么分配食物和饮料才能使得尽量多的牛都能得到自己喜欢的?
仔细想一想的话应该是网络流没错。可是应该怎么建图呢?
普通建图一般都是源点与供应相连接,汇点与需求相连接,可是此题中有两种供应食物和饮料。因为只有牛与食物和饮料都有关系,而食物和饮料之间没有必然联系,所以可以用牛将这两种供应串起来,即建图就变为:源点->食物->牛->饮料->汇点。可是为什么不是将源点与牛相连,牛与食物和饮料相连呢?因为一旦是这种连法的话,即:源点->牛->食物和饮料->汇点,就不能保证一种食物和一种饮料了,因为它有可能选择2种食物0种饮料或者0种食物2种饮料。
那么到此为止图已经建好了吗?看起来挺完美的,其实还差了一点,就是要将牛拆点:源点->食物->牛->牛->饮料->汇点。它们之间所有的流量都是1.到此为止图才算真正的建完,很多人就有疑问,为什么要拆点呢?
为什么要拆点一开始我也不明白,网上大部分的解题报告都没有写为什么要拆点,查了很多资料才略懂,下面是我对此题拆点的理解。因为一头牛要保证只有一份食物和一份饮料,而食物和牛相连能够保证是一份,那么牛与饮料相连就能保证是一份吗?不能保证,所以要拆点,使牛与牛之间的流量为1,这个1就是要控制只有一份饮料流向了它。
鄙人不才,便在网上百度了“什么情况下要拆点和拆点的作用是什么?”可是一直查不到,如有人知道,望指点一二三~~不胜感激!
//424K	0MS
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define inf 9999999
#define M 1007
#define MIN(a,b) a>b?b:a;
using namespace std;
struct E
{
    int v,w,next;
}edg[500000];
int dis[2000],gap[2000],head[2000],nodes;
int sourse,sink,nn;
void addedge(int u,int v,int w)
{
    edg[nodes].v=v;
    edg[nodes].w=w;
    edg[nodes].next=head[u];
    head[u]=nodes++;
    edg[nodes].v=u;
    edg[nodes].w=0;
    edg[nodes].next=head[v];
    head[v]=nodes++;
}
int dfs(int src,int aug)
{
    if(src==sink)return aug;
    int left=aug,mindis=nn;
    for(int j=head[src];j!=-1;j=edg[j].next)
    {
    	int v=edg[j].v;
    	if(edg[j].w)
        {
           if(dis[v]+1==dis[src])
           {
               int minn=MIN(left,edg[j].w);
               minn=dfs(v,minn);
               edg[j].w-=minn;
               edg[j^1].w+=minn;
               left-=minn;
               if(dis[sourse]>=nn)return aug-left;
               if(left==0)break;
           }
           if(dis[v]<mindis)
           mindis=dis[v];
        }
    }

        if(left==aug)
        {
            if(!(--gap[dis[src]]))dis[sourse]=nn;
            dis[src]=mindis+1;
            gap[dis[src]]++;
        }
        return aug-left;
}
int sap(int s,int e)
{
    int ans=0;
	nn=e+1;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    gap[0]=nn;
    sourse=s;
    sink=e;
    while(dis[sourse]<nn)
    ans+=dfs(sourse,inf);
    return ans;
}
int main()
{
    int n,f,d;
    while(scanf("%d%d%d",&n,&f,&d)!=EOF)
    {
        int s=0,t=n+n+f+d+1;
        nodes=0;
        int food,drink,a,b;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&food,&drink);
            for(int j=1;j<=food;j++)
            {
                scanf("%d",&a);
                addedge(a,f+i,1);//食物和牛相连
            }
            for(int j=1;j<=drink;j++)
            {
                scanf("%d",&b);
                addedge(f+n+i,f+n+n+b,1);//牛和饮料相连
            }
        }
        for(int i=1;i<=f;i++)addedge(s,i,1);//源点和食物相连
        for(int i=1;i<=d;i++)addedge(f+n+n+i,t,1);//饮料和汇点相连
        for(int i=1;i<=n;i++)addedge(f+i,f+n+i,1);//牛和牛相连
        printf("%d\n",sap(s,t));
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值