caioj.cn 1116: 网络流入门2:Dining晚餐

1116: [视频]网络流入门2:Dining晚餐

时间限制: 2 Sec   内存限制: 128 MB
提交: 212   解决: 99
[ 提交][ 状态][ 讨论版]

题目描述

【题目描述】
有 F (1 ≤ F ≤ 1000)块不同的肉(编号1~F) 和 D (1 ≤ D ≤ 1000) 罐不同的饮料(编号1~D)。有  N (1 ≤ N ≤ 1000)头牛(编号1~N)。
每头牛有自己喜欢的肉和饮料。每块肉和每罐饮料只能供给一头牛使用。
求最多能满足多少头牛能同时享用到自己喜欢的肉和饮料。(注意某头牛得到满足,不要求享用自己所有喜欢的肉和饮料,只要喜欢的肉的其中一块和自己喜欢的饮料其中一罐就可以算满足)
【输入格式】
第一行:三个整数 N, F, and D 
下来N行。每行描述一头牛。每行开头两个整数Fi和Di,Fi表示该牛喜欢的肉的数目,Di表示它喜欢的饮料的数目。下来Fi个数,各表示它喜欢的肉的编号,再来Di个数,表示它喜欢的饮料的编号。(注意Fi和Di有可能为0)
【输出格式】
一个整数,最大满足的牛的数目。(免费提示:答案中的牛必须同时享用肉和饮料,有些牛Fi或Di为0,是绝对不能选的)
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

这道题一眼看出是网络流,设置一个超级原点和一个超级汇点,再把牛、食物、饮料之间连起来,求一次最大流就好了


这是样例的图,因为食物和饮料之间没有联系,所以只能把牛放在中间

但是这样是不对

因为每一头牛只能吃一份食物,一份饮料,但是这个图可能会把

几个饮料放连到一头牛的身上

所以需要拆点,给出拆点以后的图片


在这里每一条边的流量为1(控制流量)

把牛拆开以后就能保证经过牛的流量最多为1(有一条流量为1的边,排除多余的流量)

为什么饮料却不用拆点?

因为

从牛出来的流量最多为1,可以保证一头牛只能对应一个饮料

饮料连到超级汇点的边也只有1,也可以保证不会出现两头牛同时吃一个饮料的情况

直接上代码

#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
	int x,y,c,next,other;
}a[21000];int len,last[21000],st,ed;
inline void ins(int x,int y,int c)
{
	len++;int k1=len;
	a[len].x=x;a[len].y=y;a[len].c=c;
	a[len].next=last[x];last[x]=len;
	
	len++;int k2=len;
	a[len].x=y;a[len].y=x;a[len].c=0;
	a[len].next=last[y];last[y]=len;
	
	a[k1].other=k2;
	a[k2].other=k1;
}
int head,tail,list[21000],h[21000];
inline bool bt_h()
{
	memset(h,0,sizeof(h));h[st]=1;
	list[1]=st;head=1;tail=2;
	while(head!=tail)
	{
		int x=list[head];
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(a[k].c>0 && h[y]==0)
			{
				h[y]=h[x]+1;
				list[tail++]=y;
			}
		}
		head++;
	}
	if(h[ed]>0) return true;
	return false;
}
int mymin(int x,int y){return x<y?x:y;}
int findflow(int x,int f)
{
	if(x==ed) return f;
	int s=0,t;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(a[k].c>0 && h[y]==h[x]+1 && s<f)
		{
			s+=(t=findflow(y,mymin(a[k].c,f-s)));
			a[k].c-=t;a[a[k].other].c+=t;
		}
	}
	if(s==0) h[x]=0;
	return s;
}
int main()
{
	int n,f,d;
	scanf("%d%d%d",&n,&f,&d);
	st=0;ed=2*n+f+d+1;
	for(int i=1;i<=f;i++) ins(st,i,1);//建立起点到食物的边 
	for(int i=1;i<=n;i++) ins(i+f,i+f+n,1);//把一头牛拆成牛1和牛2,中间一条流量为1的边 
	for(int i=1;i<=n;i++)
	{
		int ff,dd;
		scanf("%d%d",&ff,&dd);//食物和饮料 
		for(int j=1;j<=ff;j++)
		{
			int x;scanf("%d",&x);
			ins(x,i+f,1);//食物和牛1之间建边 
		}
		for(int j=1;j<=dd;j++)
		{
			int x;scanf("%d",&x);
			ins(n+i+f,2*n+f+x,1);//牛2和饮料之间建边 
		}
	}
	for(int i=1;i<=d;i++) ins(i+2*n+f,ed,1);//饮料和超级汇点之间建边 
	int ans=0;//最后跑一次网络流就可以了,完全是直接套进去的 
	while(bt_h()==true) ans+=findflow(st,2147483647);
	printf("%d\n",ans);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/zsyzclb/article/details/80965443