[洛谷 P2891][caioj 1116][USACO07OPEN] 吃饭Dining --- dinic最大流

【题目描述】

有 F (1 ≤ F ≤ 1000)块不同的肉(编号1~F) 和 D (1 ≤ D ≤ 1000) 罐不同的饮料(编号1~D)。有 N (1 ≤ N ≤ 1000)头牛(编号1~N)。
每头牛有自己喜欢的肉和饮料。每块肉和每罐饮料只能供给一头牛使用。
求最多能满足多少头牛能同时享用到自己喜欢的肉和饮料。(注意某头牛得到满足,不要求享用自己所有喜欢的肉和饮料,只要喜欢的肉的其中一块和自己喜欢的饮料其中一罐就可以算满足)
【输入格式】
第一行:三个整数 N, F, and D
下来N行。每行描述一头牛。每行开头两个整数Fi和Di,Fi表示该牛喜欢的肉的数目,Di表示它喜欢的饮料的数目。下来Fi个数,各表示它喜欢的肉的编号,再来Di个数,表示它喜欢的饮料的编号。(注意Fi和Di有可能为0)
【输出格式】
一个整数,最大满足的牛的数目。(免费提示:答案中的牛必须同时享用肉和饮料,有些牛Fi或Di为0,是绝对不能选的)
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

分析

建图: 源点->食物->牛1->牛2->饮料->汇点 {边权均为1}
   此处将牛拆为两点,从而满足牛只使用一样食物的限制

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>

#define IL inline
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);

#define INF 0x7f7f7f7f

using namespace std;

inline int read()
{
    char c=getchar();
    int sum=0,k=1;
    for(;'0'>c || c>'9';c=getchar())
        if(c=='-') k=-1;
    for(;'0'<=c && c<='9';c=getchar()) sum=sum*10+c-'0';
    return sum*k;
}

struct Edge
{
	int to, cap, flow;
};

int n, m, S, T;
vector<Edge> edge;
vector<int> G[50005];
int cur[50005];
int dep[50005];

IL int min_(int x,int y) { return x < y ? x : y; }

IL void add(int u, int v, int c)
{
	edge.push_back((Edge){v, c, 0});
	edge.push_back((Edge){u, 0, 0});
	G[u].push_back(m++);
	G[v].push_back(m++);
}

IL bool Bfs()
{
	queue<int>q;
	memset(dep, 0, sizeof(dep));
	q.push(S);
	dep[S] = 1;
	for(int t = 1, u; t;)
	{
		u = q.front(); q.pop(); --t;
		for(int i = 0, s = G[u].size(); i < s; ++i)
		{
			Edge e = edge[G[u][i]];
			if(!dep[e.to] && e.flow < e.cap)
			{
				q.push(e.to); ++t;
				dep[e.to] = dep[u] + 1;
			}
		}
	}
	return dep[T];
}

IL int Dfs(int u, int a)
{
	if(u == T || !a) return a;
	int flow = 0, f;
	for(int &i = cur[u], s = G[u].size(); i < s; ++i)
	{
		Edge &e = edge[G[u][i]];
		if(dep[e.to] == dep[u] + 1 && (f = Dfs(e.to, min_(a, e.cap - e.flow))) > 0)
		{
			e.flow += f;
			edge[G[u][i] ^ 1].flow -= f; 
			flow += f;
			a -= f;
			if(!a) break;
		}
	}
	return flow;
}

IL int maxflow()
{
	int flow = 0;
	for(; Bfs();)
	{
		memset(cur, 0, sizeof(cur));
		flow += Dfs(S, INF);
	}
	return flow;
}

int main()
{
    open("1116");

	int n1 = read(), n2 = read(), n3 = read();
	S = 0; T = (n1 << 1) + n2 + n3 + 1;
	for(int i = 1; i <= n2; ++i) add(S, (n1 << 1) + i, 1);
	for(int i = 1; i <= n3; ++i) add((n1 << 1) + n2 + i, T, 1);
	for(int i = 1, x, y, z, f, p1, p2; i <= n1; ++i)
	{
		x = read(); y = read();
		f = (x && y);
		
		p1 = (i << 1) - 1;
		p2 = p1 + 1;
		if(f) add(p1, p2, 1);
		
		for(; x; --x)
		{
			z = read();
			if(f) add((n1 << 1) + z, p1, 1);
		}
		
		for(; y; --y)
		{
			z = read();
			if(f) add(p2, (n1 << 1) + n2 + z, 1);
		}
	}

	printf("%d\n", maxflow());

    close;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值