POJ 3281:最大流

题意:有N个牛,F个食物,D个饮料,每个牛喜欢一些食物和饮料,现在要给牛分发食物和饮料,当一个牛得到一个喜欢的食物和一个喜欢的饮料的时候,就说这个牛被满足了。求最多可以满足多少个牛。每个饮料和食物只能被分给一个牛,每个牛也只能拿到一个饮料和食物。


题解:按流建图,一个合法的流应该是:一个牛以及一个喜欢的饮料一个喜欢的食物。一头牛贡献一单位的流量,所以把牛拆了,中间加一个1容量的边,然后起点到所有饮料连1容量边,饮料到喜欢自己的牛连1容量边,牛到他喜欢的食物连1容量的边,所有食物向终点连1容量的边。这样一个流就必须经过 饮料-牛-食物,且这个饮料和食物都是牛喜欢的,而且把牛拆了可以起到流量限制的作用,使得一个牛只占有一个饮料和食物,不会暴饮暴食(雾。


Code:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 105;
int tot,first[maxn*4],nxt[maxn*maxn*16],des[maxn*maxn*16],flow[maxn*maxn*16];
int dep[maxn*4];
const int INF = 0x3f3f3f3f;
int d,f,n;
int ss,tt;
inline void addEdge(int x,int y,int z){
	tot++;
	des[tot] =y;
	flow[tot] =z;
	nxt[tot] = first[x];
	first[x] = tot;
	
	tot++;
	des[tot] =x;
	flow[tot] =0;
	nxt[tot] = first[y];
	first[y] =tot;
}
void input(){
	memset(first,-1,sizeof first);
	scanf("%d%d%d",&n,&f,&d);
	for (int i=1;i<=n;i++){
		int di,fi,x;
		scanf("%d%d",&fi,&di);
		for (int j=0;j<fi;j++){
			scanf("%d",&x);
			addEdge(2*n+x,i,1);
		}
		for (int j=0;j<di;j++){
			scanf("%d",&x);
			addEdge(n+i,2*n+f+x,1);
		}
	}
	for (int i=1;i<=f;i++){
		addEdge(0,2*n+i,1);
	}
	for (int i=1;i<=d;i++){
		addEdge(2*n+f+i,2*n+f+d+1,1);
	}
	for (int i=1;i<=n;i++){
		addEdge(i,i+n,1);
	}
}
bool bfs(){
	queue<int> Q;
	memset(dep,-1,sizeof dep);
	dep[ss] =0;
	Q.push(ss);
	while (!Q.empty()){
		int q = Q.front();Q.pop();
		for (int t = first[q];t!=-1;t=nxt[t]){
			int v = des[t];
			int c = flow[t];
			if (c&&dep[v]==-1){
				dep[v] = dep[q]+1;
				Q.push(v);
			}
		}
	}
	return dep[tt]!=-1;
}
int dfs(int node,int now){
	if (node==tt)return now;
	int ret =0;
	for (int t = first[node];t!=-1&&ret<now;t= nxt[t]){
		int v = des[t];
		int c = flow[t];
		if (c&&dep[v]==dep[node]+1){
			int x = min(c,now-ret);
			x = dfs(v,x);
			flow[t]-=x;
			flow[t^1]+=x;
			ret +=x;
		}
	}
	if (!ret) dep[node] =-1;
	return ret;
}
int max_flow(){
	int tot =0;
	while (bfs()){
		int del =0;
		while (del = dfs(ss,INF)){
			tot+=del;
		}
	}
	return tot;
}
void solve(){
	ss =0;
	tt =2*n+f+d+1;
	printf("%d\n",max_flow());
}
int main(){
	tot =-1;
	input();
	solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值