(拆点+最大流)POJ3281 Dining

题目大意是N头牛,准备了F种食物,D种饮料,每一头牛会喜欢若干种食物和饮料,但它只能选择一种食物和一种饮料,且每种食物和饮料都只够一头牛选择,问怎样分配能使得食物和饮料都能得到的牛的数量最多,求这个数。

明显的一道最大流的题目,难点只在怎么建模。建模的方法如下:

1.建立一个超级源,跟每种食物之间连一条容量为1的边;

2.建立一个超级汇,它与每种饮料之间有一条容量为1的边;

3.将每头牛都拆分成两个点C1、C2,两点之间有一条容量为1的边;

4.若一头牛喜欢食物f,就将其对应的C1点与f连接起来,容量为1,若一头牛喜欢饮料d,同理将C2与d连接起来。

为何要拆点?

不拆点不能保证牛只能选择一种食物和一种饮料这一条件。即限定牛结点的容量为1。


至于最大流方面,数据规模不大就选了EK,当然这类题目还有变种,就要考虑其余的算法及优化手段了。


代码如下:

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int map[500][500];
int pre[500];
int n,N,D,F;
queue<int>q; 

bool bfs(int src,int des){
	memset(pre,-1,sizeof(pre));
	while (!q.empty()) q.pop();
	pre[src]=-1;
	int index;
	q.push(src);
	while(!q.empty()){
		index=q.front();
		q.pop();
		for (int i=0;i<=1+n;i++){
			if (pre[i]==-1 && map[index][i]>0){
				pre[i]=index;
				if (i==des)
					return true;
				q.push(i);
			}
		}
	}
	return false;
}

int maxflow(int src,int des){
	int maxf=0;
	while (bfs(src,des)){
		int minflow=10000000;
		for (int i=des;i!=src;i=pre[i])
			minflow=min(minflow,map[pre[i]][i]);
		for (int i=des;i!=src;i=pre[i]){
			map[pre[i]][i]-=minflow;
			map[i][pre[i]]+=minflow;
		}
		maxf+=minflow;
	}
	return maxf;
}

int main(){
	int s=0,t;
	scanf("%d%d%d",&N,&F,&D);
	n=N*2+F+D;
	memset(map,0,sizeof(map));
	t=n+1;
	for (int i=1;i<=F;i++)
		map[s][i]=1;
	for (int i=1+F+2*N;i<=D+F+2*N;i++)
		map[i][t]=1;
	for (int i=1+F;i<=N+F;i++)
		map[i][i+N]=1;
	for (int i=1;i<=N;i++){
		int f,d,id;
		scanf("%d%d",&f,&d);
		for (int j=0;j<f;j++){
			scanf("%d",&id);
			map[id][i+F]=1;
		}
		for (int j=0;j<d;j++){
			scanf("%d",&id);
			map[i+F+N][id+F+2*N]=1;
		}
	}

	printf("%d\n",maxflow(s,t));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值