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

来源:互联网 发布:淘宝售后率是什么意思 编辑:程序博客网 时间:2024/06/05 20:20

题目大意是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;}


0 0
原创粉丝点击