Dining(最大流-拆点-EK算法-Dinic算法)
judge:POJ 3281
Time Limit: 2000MS
Memory Limit: 65536K
source:USACO 2007 Open Gold
Description
Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will consume no others.……
POJ 3281
Input
Output
Sample Input
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
Sample Output
3
Hint
题意
有n头牛,f种饮料和d种食物
每头牛都有自己想要的饮料和食物(一种或好几种),但是每一种饮料和食物只能被一头牛选择,问你最大能满足多少头牛的要求。
最开始想的是分别对饮料和食物与牛进行最大匹配,但是为了让饮料和食物也能对应上,于是我想到了这样建图:先把饮料与源点相连,再把饮料与牛相连,再把牛与食物相连,最后把食物和汇点相连。这样求最大匹配就OK了。
后来发现这样做有BUG,因为每头牛只能选一种饮料和一种食物,而不给牛加限制的话就会出现这种情况:
所以为了限制奶牛的流量,故对奶牛进行拆点:拆成前后两部分,前一部分与饮料相连,后一部分与食物相连,并且对每一条边的容量设置成1,于是就不会溢出了。
至于每个点的编号,这个就看个人喜好吧,只要任何两个点的编号不重复就OK。
由于本人也是初学者,所以分别用两种算法分别实现了一下。下面给出两种算法的代码
EK算法版(结构较简单,适用于简单的网络流)
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#define _for(i, a) for(int i = 0; i < (a); i++)
#define _rep(i, a, b) for(int i = (a); i <= (b); i++)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 10005;
using namespace std;
struct poi {
int u, v, cap, next;//w 0 drinks 1 vegetable
}edges[maxn];
int head[maxn], cnt;
int n, f, d;
int dis[maxn];
int S, T;
int p[maxn];
void init() {
memset(head, -1, sizeof(head));
memset(edges, 0, sizeof(edges));
cnt = 0;
S = 2 * n + f + d + 1;
T = S + 1;
}
void adde(int u, int v, int w) {
edges[cnt].u = u, edges[cnt].v = v, edges[cnt].cap = w;
edges[cnt].next = head[u], head[u] = cnt++;
edges[cnt].u = v, edges[cnt].v = u, edges[cnt].cap = 0;
edges[cnt].next = head[v], head[v] = cnt++;
}
int EK() {
int flow = 0;
while (1) {
memset(dis, 0, sizeof(dis));
queue<int> Q;
Q.push(S);
dis[S] = inf;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = edges[i].next) {
int v = edges[i].v, cap = edges[i].cap;
if (!dis[v] && cap > 0) {
p[v] = i;
dis[v] = min(dis[u], cap);
Q.push(v);
}
}
if (dis[T]) break;
}
if (!dis[T]) break;
for (int u = T; u != S; u = edges[p[u]].u) {
edges[p[u]].cap -= dis[T];
edges[p[u] ^ 1].cap += dis[T];
}
flow += dis[T];
}
return flow;
}
int main() {
//freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
while (cin >> n >> f >> d) {
init();
_rep(i, 2 * n + 1, 2 * n + f) {
adde(S, i, 1);
}
_rep(i, 1, n) {
adde(i, i + n, 1);
}
_rep(i, 2 * n + f + 1, 2 * n + f + d) {
adde(i, T, 1);
}
_rep(i, 1, n) {
int fi, di;
cin >> fi >> di;
_rep(j, 1, fi) {
int v;
cin >> v;
adde(v + 2 * n, i, 1);
}
_rep(j, 1, di) {
int v;
cin >> v;
adde(i + n, v + 2 * n + f, 1);
}
}
printf("%d\n", EK());
}
return 0;
}
Dinic算法版(结构较复杂,适用性强)
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#define _for(i, a) for(int i = 0; i < (a); i++)
#define _rep(i, a, b) for(int i = (a); i <= (b); i++)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 10005;
using namespace std;
struct poi {
int u, v, cap, next;//w 0 drinks 1 vegetable
}edges[maxn];
int head[maxn], cnt;
int n, f, d;
int dis[maxn];
int S, T;
int cur[maxn];
void init() {
memset(head, -1, sizeof(head));
memset(edges, 0, sizeof(edges));
cnt = 0;
S = 2 * n + f + d + 1;
T = S + 1;
}
void adde(int u, int v, int w) {
edges[cnt].u = u, edges[cnt].v = v, edges[cnt].cap = w;
edges[cnt].next = head[u], head[u] = cnt++;
edges[cnt].u = v, edges[cnt].v = u, edges[cnt].cap = 0;
edges[cnt].next = head[v], head[v] = cnt++;
}
//bfs 分层次
bool bfs() {
memset(dis, 0, sizeof(dis));
dis[S] = 1;
queue<int> Q;
Q.push(S);
int flow = 0;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = edges[i].next) {
int v = edges[i].v, cap = edges[i].cap;
if (cap > 0 && !dis[v]) {
dis[v] = dis[u] + 1;
Q.push(v);
}
}
}
return dis[T] != 0;
}
//dfs 找增广路
int dfs(int u, int mw) {
if (u == T || mw == 0) return mw;
int flow = 0;
for (int &i = cur[u]; ~i; i = edges[i].next) {//从上次考虑的弧开始
int v = edges[i].v, cap = edges[i].cap;
if (cap > 0 && dis[v] == dis[u] + 1) {
int cw = dfs(v, min(mw, cap));
flow += cw;
edges[i].cap -= cw;
edges[i ^ 1].cap += cw;
mw -= cw;
if (mw == 0) break;
}
}
return flow;
}
//主进程
int dinic() {
int res = 0;
while (bfs()) {
memcpy(cur, head, sizeof(head));
res += dfs(S, inf);
}
return res;
}
int main() {
//freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
while (cin >> n >> f >> d) {
init();
_rep(i, 2 * n + 1, 2 * n + f) {
adde(S, i, 1);
}
_rep(i, 1, n) {
adde(i, i + n, 1);
}
_rep(i, 2 * n + f + 1, 2 * n + f + d) {
adde(i, T, 1);
}
_rep(i, 1, n) {
int fi, di;
cin >> fi >> di;
_rep(j, 1, fi) {
int v;
cin >> v;
adde(v + 2 * n, i, 1);
}
_rep(j, 1, di) {
int v;
cin >> v;
adde(i + n, v + 2 * n + f, 1);
}
}
printf("%d\n", dinic());
}
return 0;
}