BZOJ 1711 [Usaco2007 Open]Dining吃饭 最大流

Description

农夫JOHN为牛们做了很好的食品,但是牛吃饭很挑食. 每一头牛只喜欢吃一些食品和饮料而别的一概不吃.虽然他不一定能把所有牛喂饱,他还是想让尽可能多的牛吃到他们喜欢的食品和饮料. 农夫JOHN做了F (1 <= F <= 100) 种食品并准备了D (1 <= D <= 100) 种饮料. 他的N (1 <= N <= 100)头牛都以决定了是否愿意吃某种食物和喝某种饮料. 农夫JOHN想给每一头牛一种食品和一种饮料,使得尽可能多的牛得到喜欢的食物和饮料. 每一件食物和饮料只能由一头牛来用. 例如如果食物2被一头牛吃掉了,没有别的牛能吃食物2.

Input

* 第一行: 三个数: N, F, 和 D

* 第2..N+1行: 每一行由两个数开始F_i 和 D_i, 分别是第i 头牛可以吃的食品数和可以喝的饮料数.下F_i个整数是第i头牛可以吃的食品号,再下面的D_i个整数是第i头牛可以喝的饮料号码.

Output

* 第一行: 一个整数,最多可以喂饱的牛数.

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总食品和饮料).当然,别的数据会更难.

HINT








……看上去就是一个“三分图匹配”??
比较裸的最大流。。

建图错了好几次= =
注意每头牛只会吃一份食物。。。



#include<bits/stdc++.h>
using namespace std;
const int 
	N=105,
	M=405,
	inf=100000000;
int n,F,D,Ecnt;
int source,sink,POINTS;
int d[M],gap[M],Q[M],pre[M];
struct Edge{
	int next,to,C;
}E[(N+(N*N<<1))<<1];int head[M],cur[M];
void add(int u,int v,int C){
	E[Ecnt].next=head[u];
	E[Ecnt].to=v;
	E[Ecnt].C=C;
	head[u]=Ecnt++;
}
void BFS(){
	memset(d,255,sizeof(d));
	memset(gap,0,sizeof(gap));
	gap[0]=1,d[sink]=0;
	int h=0,t=1;Q[0]=sink;
	while (h<t){
		int u=Q[h++];
		for (int i=head[u];~i;i=E[i].next){
			int v=E[i].to;
			if (~d[v]) continue;
			d[v]=d[u]+1,gap[d[v]]++;
			Q[t++]=v;
		}
	}
}
int ISAP(){
	BFS();
	memcpy(cur,head,sizeof(cur));
	int flow=0,u=source;pre[u]=u;
	while (d[sink]<POINTS+1){
		if (u==sink){
			int f=inf,neck;
			for (int i=source;i!=sink;i=E[cur[i]].to)
				if (f>E[cur[i]].C) f=E[cur[i]].C,neck=i;
			for (int i=source;i!=sink;i=E[cur[i]].to)
				E[cur[i]].C-=f,E[cur[i]^1].C+=f;
			flow+=f,u=neck;
		}
		int j;
		for (j=cur[u];~j;j=E[j].next)
			if (E[j].C && d[E[j].to]+1==d[u]) break;
		if (~j) pre[E[j].to]=u,cur[u]=j,u=E[j].to;
		 else{
			if (!(--gap[d[u]])) break;
			int mind=POINTS+1;
			for (int i=head[u];~i;i=E[i].next)
				if (E[i].C && mind>d[E[i].to])
					mind=d[E[i].to],cur[u]=i;
			d[u]=mind+1,gap[d[u]]++;
			u=pre[u];
		}
	}
	return flow;
}
void build(){
	memset(head,255,sizeof(head));
	int tf,td,x;
	POINTS=(n<<1)+F+D+2,Ecnt=0;
	source=(n<<1)+F+D+1,sink=source+1;
	for (int i=1;i<=n;i++){
		scanf("%d%d",&tf,&td);
		add((i<<1)-1,i<<1,1),add(i<<1,(i<<1)-1,0);
		for (int j=1;j<=tf;j++){
			scanf("%d",&x);
			add(i<<1,x+(n<<1),1),add(x+(n<<1),i<<1,0);
		}
		for (int j=1;j<=td;j++){
			scanf("%d",&x);
			add(x+(n<<1)+F,(i<<1)-1,1),add((i<<1)-1,x+(n<<1)+F,0);
		}
	}
	for (int i=(n<<1)+F+1;i<=(n<<1)+F+D;i++) add(source,i,1),add(i,source,0);
	for (int i=(n<<1)+1;i<=(n<<1)+F;i++) add(i,sink,1),add(sink,i,0);
}
int main(){
	scanf("%d%d%d",&n,&F,&D);
	build();
	printf("%d\n",ISAP());
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值