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
有 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;
}