bzoj 1711: [Usaco2007 Open]Dining吃饭

本文针对USACO 2007 Open赛题“Dining”进行了解析,介绍了如何通过构建三分图匹配模型使用网络流算法解决该问题。详细展示了构建网络流图的具体步骤,并提供了一段完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1711: [Usaco2007 Open]Dining吃饭

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 1055   Solved: 567
[ Submit][ Status][ Discuss]

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

Source


看到这数据范围就想到网络流

好久没写了QAQ

板子都快忘了QAQ

主要是难在建图啦

刚开始没想出来

看了题解QAQ

传说中的三分图匹配2333

建图方式摘自hzwer博客:

每头牛拆成x和x’,相互连1的边

源向食物连1的边,饮料向汇连1的边

然后牛和可以吃的饮料/食物互相连边

最大流为答案

#include<cstdio>
#include<algorithm>
#include<cstring>
const int inf=2*1e9,N=500,M=30000,len=3000;
int S,T;
struct node
{
	int to,next,v;
}e[M*2];
int cnt=1;
int first[N],cur[N],dis[N],qu[len+5];
inline void insert(int u,int v,int q)
{
	e[++cnt]=(node){v,first[u],q};first[u]=cnt;
	e[++cnt]=(node){u,first[v],0};first[v]=cnt;
//	printf("std::	%d %d %d\n",u,v,q);
}
bool bfs()
{
	memset(dis,-1,sizeof(dis));
	int head=1,tail=2;
	qu[1]=S;dis[S]=0;
	while(head!=tail)
	{
		int rr=qu[head++];if(head==len)	head=0;
		for(int k=first[rr];k;k=e[k].next)
		if(dis[e[k].to]==-1&&e[k].v)
		{
			dis[e[k].to]=dis[rr]+1;
			qu[tail++]=e[k].to;if(tail==len)tail=0;
		}
	}
	return dis[T]==-1?0:1;
}
int dfs(int x,int a)
{
	if(x==T||a==0)	return a;
	int flow=0;
	for(int &k=cur[x];k;k=e[k].next)
	if(e[k].v&&dis[e[k].to]==dis[x]+1)
	{
		int t=dfs(e[k].to,std::min(e[k].v,a));
		e[k].v-=t,e[k^1].v+=t;
		flow+=t,a-=t;
		if(!a)	break;
	}
	if(!flow)	dis[x]=-1;
	return flow;
}
int main()
{
	int n,f,d,k,p,q;
	scanf("%d %d %d",&n,&f,&d);
	S=0;T=f+2*n+d+1;
	for(int i=1;i<=f;i++)	insert(S,i,1);
	for(int i=1;i<=d;i++)	insert(f+2*n+i,T,1);	
	for(int i=1;i<=n;i++)	insert(f+i,f+n+i,1);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&p,&q);
		for(int j=1;j<=p;j++)	scanf("%d",&k),insert(k,f+i,1);
		for(int j=1;j<=q;j++)	scanf("%d",&k),insert(f+n+i,f+2*n+k,1);
	}
	int ans=0;
	while(bfs())	
	{
//		printf("a");
		for(int i=S;i<=T;i++)	cur[i]=first[i];
		ans+=dfs(S,inf);
	}
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值