poj 3281

来源:互联网 发布:java数组的逆序输出 编辑:程序博客网 时间:2024/05/21 21:37

题目概述

有N头牛,F种食品和D种饮料,均从1开始连续编号,每种吃的喝的只能分给一头牛,每头牛也只能享用一种食品一种饮料,每头牛只喜欢其中f种吃的和d种喝的,问最多可满足多少牛的喜好

时限

2000ms/6000ms

输入

第一行三个整数N,F,D,其后N行,每行前两个整数f,d,其后f个整数,代表该牛喜欢的吃的,其后d个整数,代表该牛喜欢喝的,输入只有一组

限制

1<=N<=100;1<=F<=100;1<=D<=100

输出

一个数,为所求最大满足数

样例输入

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

样例输出

3

讨论

图论,网络流,最大流,Isap+gap优化,网络流最烦人的地方全是构图,一般的供需问题都是一类供应一类需求,比如poj 1698那种,一边是天数,一边是电影,这题是一对二的关系,因而需要有所调整,一开始可能会按照传统方法构图,最左边源点,然后牛,然后吃的,然后喝的,然后汇点,也很容易看出不行,因为吃的喝的相连无法体现牛和其喜欢的饮料之间的关系,要保持牛和吃的喝的之间的对应关系,也就是要让牛直接和吃的喝的相连,那把牛放中间就行了吗?也不行,别忘了牛只能各选一种,而作为一个节点的话很可能有多条边经过,也就是不小心享用了好几种,要体现牛只能选一种,没法直接限制多少边经过同一个节点,但可以通过流量限制,将每头牛拆成两个点,加一条容量只有1的边,这样确保了最多只有一条边经过(这里默认源点到吃的的边和喝的到汇点的边容量都是1),最后确认一下,最左边源点,然后吃的,边容量1,然后牛,边容量1,然后还是牛,容量1,然后喝的,1,然后汇点,都是1,而且牛只能选一种,每种也只能给一头牛,关系明了,构图完成,往后就不用说了
实现层面上,在构图时需要谨慎一些,因为所有点都在一张图上,要明确哪些下标都是什么东西,源点汇点放哪合适,N到底是多少,怕乱套就找张纸都写下来
一开始额想的是构造两张图分别求最大流最后取最小值,显然不对,当出现一张图中某牛只有自己喜欢吃的,另一张图另一头牛只有自己喜欢喝的,结果就会大1
网络流的题额不太想用邻接表,因为这会给debug带来很大麻烦,而且由于复杂度原因一般数据规模不会太大,内存也都能吃的开,遍历的额外开销也可以接受,尽量还是以矩阵解决

题解状态

824K,16MS,C++,1675B

题解代码

#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;#define INF 0x3f3f3f3f#define MAXN 404#define memset0(a) memset(a,0,sizeof(a))int N, F, D, S, T;//牛数 食品数 饮料数 源点 汇点int R[MAXN][MAXN], dis[MAXN], from[MAXN], gap[MAXN];//isap四大数组 残量矩阵 层次 父节点 每个层次节点数queue<int>q;int fun(){    T = 2 * N + D + F + 1;//源点放在0 汇点在最后一个    for (int p = 1; p <= F; p++)        R[S][p] = 1;//构造源点到食品的边    for (int p = 2 * N + F + 1; p < T; p++)        R[p][T] = 1;//构造饮料到汇点的边    for (int p = F + 1; p <= N + F; p++)        R[p][N + p] = 1;//构造牛到自己的边    for (int p = 1; p <= N; p++) {        int f, d, a;        scanf("%d%d", &f, &d);//input        while (f--) {            scanf("%d", &a);//input            R[a][F + p] = 1;//构造食品到牛的边        }        while (d--) {            scanf("%d", &a);//input            R[F + N + p][F + 2 * N + a] = 1;//构造牛到饮料的边        }    }    N = T + 1;//为了防止乱套 这回最后再修改N    for (int p = 0; p < N; p++)//往后……很熟悉的isap 不想写了        dis[p] = N;    dis[T] = 0;    gap[0]++;    q.push(T);    while (!q.empty()) {        int a = q.front();        q.pop();        for (int p = 0; p < N; p++)            if (dis[p] == N&&R[p][a]) {                q.push(p);                dis[p] = dis[a] + 1;                gap[dis[p]]++;            }    }    int most = 0, p = S, flow = 1;//既然所有边容量都是1 也没必要搞成正无穷了    while (dis[S] < N) {        int i;        for (i = 0; i < N && (!R[p][i] || dis[p] != dis[i] + 1); i++);        if (i != N) {            from[i] = p;            flow = min(flow, R[p][i]);            p = i;            if (p == T) {                while (p != S) {                    i = from[p];                    R[i][p] -= flow;                    R[p][i] += flow;                    p = i;                }                most += flow;                flow = 1;            }        }        else {            if (!--gap[dis[p]])                break;            dis[p] = N;//这里用INF会RE 当牛没有喜欢的饮料时 只有推进时的一条边相连 但不满足下面第一个条件 然后gap递增时会严重越界            for (int i = 0; i < N; i++)                if (R[p][i] && dis[p] > dis[i] + 1)                    dis[p] = dis[i] + 1;            gap[dis[p]]++;            if (p != S)                p = from[p];        }    }    return most;}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    scanf("%d%d%d", &N, &F, &D);//input    printf("%d\n", fun());//output}

EOF

0 0
原创粉丝点击