POJ3281——巧妙建图!!

题目链接:http://poj.org/problem?id=3281

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: N, F, 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.

题目翻译

农夫为他的 N (1 ≤ N ≤ 100) 牛准备了 F (1 ≤ F ≤ 100)种食物和 D (1 ≤ D ≤ 100) 种饮料。每头牛都有各自喜欢的食物和饮料,而每种食物或饮料只能分配给一头牛。最多能有多少头牛可以同时得到喜欢的食物和饮料?

Input

第一行输入三个整数N, F, D

接下来n行,每行先输入两个整数 Fi 和 Di,分别表示编号为 i 的牛喜欢的食物和饮料的数量,接下来的Fi个整数表示第i头牛喜欢的食物的编号,最后Di个整数表示第i头牛喜欢的饮料的编号。

Output

输出同时得到喜欢的食物和饮料的牛的数量的最大值。

 

比较经典而且巧妙地建图题。首先把牛u拆成两个点u1和u1+n,而且两个点建边,权值为1,一头牛只能要一个喜欢的食物和饮料。之后再设一个超级源点s=0,把每个食物和源点相连,权值为1,表示1个食物,以及一个超级汇点s=2*n+food+drink+1,把每个饮料和汇点相连,表示有一个饮料,之后再把每头牛和和喜欢的食物和饮料相连。

总的建图就是,先源点s,之后食物(饮料),到牛u1,再到u1+n,然后饮料(食物),最后就是汇点t.

连完边,直接跑最大流求出结果就行。

#include <iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
int S,T;
int G[507][507],visited[507],pre[507];
int EK(){
	queue<int> q;
	int ans=0;
	while(1){
		memset(pre,-1,sizeof(pre));
		memset(visited,0,sizeof(visited));
		while(!q.empty()) q.pop();
		q.push(0);
		visited[0]=1;
		while(!q.empty()){
			int now=q.front();
			q.pop();
			if(now==T) break;
			for(int i = S;i<=T;++i){
				if(!visited[i]&&G[now][i]>0){
					pre[i]=now;
					visited[i]=1;
					q.push(i);
				}
			} 
		}
		if(!visited[T]) break;
		int minn=INF;
		for(int i = T;i!=0;i=pre[i])
			minn=min(minn,G[pre[i]][i]);
		ans+=minn;
		for(int i = T;i!=0;i=pre[i]){
			G[pre[i]][i]-=minn;
			G[i][pre[i]]+=minn;
		}
	}
	return ans;
} 
int main(int argc, char** argv) {
	int n,food,drink;
	while(~scanf("%d%d%d",&n,&food,&drink)){
		memset(G,0,sizeof(G));
		S=0;
		T=n*2+food+drink+1;
		for(int i = 1;i<=food;++i) G[S][i]=1;//源点和食物相连 
		for(int i = food+2*n+1;i<=food+2*n+drink;++i) G[i][T]=1;//汇点和饮料相连 
		for(int i = 1;i<=n;++i) G[food+i][food+n+i]=1;//牛拆的两个点建边 
		for(int i = 1;i<=n;++i){
			int x,y;
			scanf("%d%d",&x,&y);
			while(x--){
				int m;
				scanf("%d",&m);
				G[m][food+i]=1;//左边的牛u和喜欢的食物相连 
			}
			while(y--){
				int m;
				scanf("%d",&m);
				G[food+n+i][food+2*n+m]=1;//右边的牛u+n和喜欢的饮料相连 
			}
		}
		printf("%d\n",EK());
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值