本文主要是介绍2240. 餐饮(最大流,拆点),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
活动 - AcWing
奶牛们在吃饭方面十分挑剔。
每头奶牛都有自己喜欢的食物和饮料,并且不会食用其他不喜欢的食物和饮料。
农夫约翰为他的奶牛们做了美味的饭菜,但他忘了对照他们的喜好来检查菜单。
虽然他可能无法令所有奶牛满意,但他想给尽可能多的奶牛提供一顿完整的用餐----既有食物可吃,也有饮料可喝。
农夫约翰一共烹制了 F 种食物,并提供了 D 种饮料。
约翰共有 N 头奶牛,其中第 i 头奶牛有 Fi 种喜欢的食物以及 Di 种喜欢的饮料。
约翰需要给每头奶牛分配一种食物和一种饮料,并使得有吃有喝的奶牛数量尽可能大。
每种食物或饮料都只有一份,所以只能分配给一头奶牛食用。
输入格式
第一行包含三个整数 N,F,D。
接下来 N 行,其中第 i 行描述第 i 头奶牛的饮食喜好,首先包含两个整数 Fi 和 Di,表示其喜欢的食物和饮料数量,然后包含 Fi 个整数表示其喜欢的食物的种类编号,最后包含 Di 个整数表示其喜欢的饮料的种类编号。
食物编号从 1 到 F,饮料编号从 1 到 D。
输出格式
输出一个整数,表示能够有吃有喝的奶牛的最大数量。
数据范围
1≤N,F,D≤100
1≤Fi≤F
1≤Di≤D
输入样例:
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
样例解释
一种使得三头奶牛满意的可行方法是:
奶牛 1:没饭。
奶牛 2:食物 2,饮料 2。
奶牛 3:食物 1,饮料 1。
奶牛 4:食物 3,饮料 3。
解析:
本题与二分图的匹配很像,思路也差不多。
首先将点集分成三部分:牛,饮料,食物。建图方式与二分图匹配类似,但如果直接按照二分图的方式建图的话,将牛放在中间点集,就会出现多种饮料和多种食物对应一头牛,不能保证流过牛节点的流量为 1。
拆点:针对上述问题,我们可以使用拆点的方式。上述的问题是由无法确保流过牛的节点的流量为 1 引起的,所以针对此类问题,我们可以将牛节点拆成两个节点:入节点和出节点。两个节点之间连一条容量为 1 的边,这样我们就能确保流过一头牛的流量为 1 了。
建图的正确性证明与之前的最大流之二分图匹配的证明类似。
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
#include<sstream>
#include<deque>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 410, M = 40610, INF = 0x3f3f3f3f;
int n, F, D, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];void add(int a, int b, int c) {e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}bool bfs() {int hh = 0, tt = 0;memset(d, -1, sizeof d);q[0] = S, d[S] = 0, cur[S] = h[S];while (hh <= tt) {int t = q[hh++];for (int i = h[t]; i != -1; i = ne[i]) {int j = e[i];if (d[j] == -1 && f[i]) {d[j] = d[t] + 1;cur[j] = h[j];if (j == T)return 1;q[++tt] = j;}}}return 0;
}int find(int u, int limit) {if (u == T)return limit;int flow = 0;for (int i = cur[u]; i != -1 && flow < limit; i = ne[i]) {int j = e[i];cur[u] = i;if (d[j] == d[u] + 1 && f[i]) {int t = find(j, min(f[i], limit - flow));if (!t)d[j] = -1;f[i] -= t, f[i ^ 1] += t, flow += t;}}return flow;
}int dinic() {int ret = 0, flow;while (bfs())while (flow = find(S, INF)) {//cout << "_____________________ " << flow << endl;ret += flow;}return ret;
}int main() {cin >> n >> F >> D;memset(h, -1, sizeof h);S = 0, T = n * 2 + F + D + 1;for (int i = 1; i <= F; i++)add(S, 2 * n + i, 1);for (int i = 1; i <= D; i++)add(i + 2 * n + F, T, 1);for (int i = 1; i <= n; i++)add(i, i + n, 1);for (int i = 1,a,b; i <= n; i++) {scanf("%d%d", &a, &b);for (int j = 1,t; j <= a; j++) {scanf("%d", &t);add(2 * n + t, i, 1);}for (int j = 1,t; j <= b; j++) {scanf("%d", &t);add(i + n, 2 * n + F + t, 1);}}cout << dinic() << endl;return 0;
}
这篇关于2240. 餐饮(最大流,拆点)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!