题目大意:有$n$个人,$p$个房间,$q$道菜($n,p,qleqslant100$),每个人有自己喜欢的房间和菜,问最多使得多少个人高兴(即拥有喜欢的房间和菜)
题解:网络流,源点向每道菜连边,每道菜向喜欢的人连边,每个人向喜欢的房间连边,每个房间再向汇点连边。发现这样人会重复,于是把人拆点即可。
卡点:无
C++ Code:
#include <algorithm> #include <cstdio> #include <iostream> #define maxn 111 const int N = maxn << 2, M = maxn * maxn * 2 + maxn * 3; const int inf = 0x3f3f3f3f; namespace Network_Flow { int lst[N], head[N], cnt = 1; struct Edge { int to, nxt, w; } e[M << 1]; inline void addedge(int a, int b, int c = 1) { e[++cnt] = (Edge) { b, head[a], c }; head[a] = cnt; e[++cnt] = (Edge) { a, head[b], 0 }; head[b] = cnt; } int n, st, ed, MF; int GAP[N], d[N]; int q[N], h, t; void init() { GAP[d[ed] = 1] = 1; for (int i = st; i <= ed; ++i) lst[i] = head[i]; q[h = t = 0] = ed; while (h <= t) { int u = q[h++]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (!d[v]) { d[v] = d[u] + 1; ++GAP[d[v]]; q[++t] = v; } } } } int dfs(int u, int low) { if (!low || u == ed) return low; int w, res = 0; for (int &i = lst[u]; i; i = e[i].nxt) if (e[i].w) { int v = e[i].to; if (d[u] == d[v] + 1) { w = dfs(v, std::min(low, e[i].w)); res += w, low -= w; e[i].w -= w, e[i ^ 1].w += w; if (!low) return res; } } if (!(--GAP[d[u]])) d[st] = n + 1; ++GAP[++d[u]], lst[u] = head[u]; return res; } void ISAP(int S, int T) { st = S, ed = T; n = ed - st + 1; init(); while (d[st] <= n) MF += dfs(st, inf); } } using Network_Flow::addedge; int n, p, q; int main() { std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0); std::cin >> n >> p >> q; int st = 0, ed = p + q + n + n + 1; for (int i = 1; i <= p; ++i) addedge(st, i); for (int i = 1, t; i <= n; ++i) { for (int j = 1; j <= p; ++j) { std::cin >> t; if (t) addedge(j, i + p); } } for (int i = 1; i <= n; ++i) addedge(i + p, i + n + p); for (int i = 1, t; i <= n; ++i) { for (int j = 1; j <= q; ++j) { std::cin >> t; if (t) addedge(i + n + p, j + n + n + p); } } for (int i = 1; i <= p; ++i) addedge(i + n + n + p, ed); Network_Flow::ISAP(st, ed); std::cout << Network_Flow::MF << std::endl; return 0; }