poj 3281 && NYOJ 326 Dining 最大流问题 EK

本文介绍了一道关于最大流的问题,通过具体实例详细解释了如何进行问题建模,并提供了一份C语言实现的Edmonds-Karp算法代码,用于解决牛群的食物与饮料分配问题。

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

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

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

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

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

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

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

为何要拆点?

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


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


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define inf 1000
#define nMax 410
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)

int map[nMax][nMax];
int N,F,D;
int path[nMax];
int queue[nMax * 100];
int head,end;
//bool flag[nMax];
//广搜求一条增广路
int bfs()
{
	int minFlow = inf,u;
	memset(path, -1, sizeof(path));
	head = 0;
	end = 1;
	queue[head] = 0;
	while (head < end)
	{
		u = queue[head ++];
		if (u == 2 * N + F + D + 1)
		{
			break;
		}
		for (int i = 1; i <= 2 * N + F + D + 1; ++ i)
		{
			if (path[i] == -1 && map[u][i] )
			{
				if (minFlow > map[u][i])
				{
					minFlow = map[u][i];
				}
				queue[end ++] = i;
				path[i] = u;
				
			}
		}
	}
	if (path[2 * N + F + D + 1] == -1)
	{
		return -1;
	}
	return minFlow;
}
//EK算法,每次广搜得到一条增广路径,然后更新残留网络
void Edmods_Karp()
{
	int flow, maxFlow = 0, now, pre;
	while ((flow = bfs()) != -1)
	{
		maxFlow += flow;
		now = 2 * N + F + D + 1;
		while (now != 0)
		{
			pre = path[now];
			map[pre][now] -= flow;
			map[now][pre] += flow;
			now = pre;
		}
	}
	printf("%d\n", maxFlow);
}
//按照源点-食物-牛-牛-饮料-汇点的顺序建图
void buildMap()
{
	int fNum,dNum,fd;
	while (scanf("%d %d %d", &N, &F, &D) != EOF)
	{
		memset(map, 0, sizeof(map));
		//memset(flag, false, sizeof(flag));
		for (int i = 1; i <= N; ++ i)
		{
			scanf("%d %d", &fNum, &dNum);
			for (int j = 0; j < fNum; ++ j)
			{
				scanf("%d", &fd);
				map[0][fd] = 1;
				map[fd][i + F] = 1;
			}
			map[i + F][i + F + N] = 1;
			for (int j = 0; j < dNum; ++ j)
			{
				scanf("%d", &fd);
				map[fd + 2 * N + F][F + 2 * N + D + 1] = 1;
				map[i + F + N][fd + 2 * N + F] = 1;
			}
		}
		Edmods_Karp();
	}
}
//注意这里给点编号,0-源点,1-F是食物,F+1-F+N是牛左点,F+N+1-F+N+N是牛右点,F+N+N+1-F+N+N+D是drink饮料点,F+N+N+D+1是汇点

int main()
{
	buildMap();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值