题目概述
有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