您的位置:首页 > 其它

[POJ3281]Dining 最大流(建图奇葩)

2015-11-13 21:30 295 查看
  题目链接:http://poj.org/problem?id=3281

  参考了某犇做的PPT。对于此题的解释有如下内容(我只是搬运工)。

  【题目大意】 有F种食物和D种饮料,每种食物或饮料只能供一头牛享用,且每头牛只享用一种食物和一种饮料。现在有N头牛,每头牛都有自己喜欢的食物种类列表和饮料种类列表,问最多能使几头牛同时享用到自己喜欢的食物和饮料。(1 <= F <= 100, 1 <= D <= 100, 1 <= N <= 100)

  此题的建模方法比较有开创性。以往一般都是左边一个点集表示供应并与源相连,右边一个点集表示需求并与汇相连。现在不同了,供应有两种资源,需求仍只有一个群体,怎么办?其实只要仔细思考一下最大流的建模原理,此题的构图也不是那么难想。最大流的正确性依赖于它的每一条s-t流都与一种实际方案一一对应。那么此题也需要用s-t流将一头牛和它喜欢的食物和饮料“串”起来,而食物和饮料之间没有直接的关系,自然就想到把牛放在中间,两边是食物和饮料,由s, t将它们串起来构成一种分配方案。至此建模的方法也就很明显了:每种食物i作为一个点并连边(s, i, 1),每种饮料j作为一个点并连边(j, t, 1),将每头牛k拆成两个点k’, k’’并连边(k’, k’’, 1), (i, k’, 1), (k’’, j, 1),其中i, j均是牛k喜欢的食物或饮料。求一次最大流即为结果。

(牛必须放中间嗯)dinic板子,代码如下:

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef struct Edge {
int u, v, c, next;
}Edge;

const int inf = 0x7f7f7f7f;
const int maxn = 2222;

int cnt, head[maxn];
int cur[maxn], dd[maxn];
Edge edge[maxn<<1];

int N, F, D;
int n[maxn], f[maxn], d[maxn];
int S, T;

void init() {
memset(head, -1, sizeof(head));
for(int i = 0; i < maxn; i++) edge[i].next = -1;
S = 0;
}

void adde(int u, int v, int c, int c1) {
edge[cnt].u = u; edge[cnt].v = v; edge[cnt].c = c;
edge[cnt].next = head[u]; head[u] = cnt++;
edge[cnt].u = v; edge[cnt].v = u; edge[cnt].c = c1;
edge[cnt].next = head[v]; head[v] = cnt++;
}

bool bfs(int s, int t, int n) {
queue<int> q;
for(int i = 0; i < n; i++) dd[i] = inf;
dd[s] = 0;
q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; ~i; i = edge[i].next) {
if(dd[edge[i].v] > dd[u] + 1 && edge[i].c > 0) {
dd[edge[i].v] = dd[u] + 1;
if(edge[i].v == t) return 1;
q.push(edge[i].v);
}
}
}
return 0;
}

int dinic(int s, int t, int n) {
int st[maxn], top;
int u;
int flow = 0;
while(bfs(s, t, n)) {
for(int i = 0; i < n; i++) cur[i] = head[i];
u = s; top = 0;
while(cur[s] != -1) {
if(u == t) {
int tp = inf;
for(int i = top - 1; i >= 0; i--) {
tp = min(tp, edge[st[i]].c);
}
flow += tp;
for(int i = top - 1; i >= 0; i--) {
edge[st[i]].c -= tp;
edge[st[i] ^ 1].c += tp;
if(edge[st[i]].c == 0) top = i;
}
u = edge[st[top]].u;
}
else if(cur[u] != -1 && edge[cur[u]].c > 0 && dd[u] + 1 == dd[edge[cur[u]].v]) {
st[top++] = cur[u];
u = edge[cur[u]].v;
}
else {
while(u != s && cur[u] == -1) {
u = edge[st[--top]].u;
}
cur[u] = edge[cur[u]].next;
}
}
}
return flow;
}

int main() {
// freopen("in", "r", stdin);
while(~scanf("%d %d %d", &N, &F, &D)) {
init();
T = 2 * N + F + D + 1;
int ff, dd;
//把牛的位置拆开,放在食物和饮料中间
//S指向食物,饮料指向T,牛指向牛
for(int i = 1; i <= F; i++) adde(S, i, 1, 0);
for(int i = 1; i <= D; i++) adde(2*N+F+i, T, 1, 0);
for(int i = 1; i <= N; i++) adde(F+i, F+N+i, 1, 0);
//确定食物指向牛、牛指向饮料的关系
for(int i = 1; i <= N; i++) {
int tmp;
scanf("%d %d", &ff, &dd);
for(int j = 0; j < ff; j++) {
scanf("%d", &tmp);
adde(tmp, F+i, 1, 0);
}
for(int j = 0; j < dd; j++) {
scanf("%d", &tmp);
adde(F+N+i, 2*N+F+tmp, 1, 0);
}
}
printf("%d\n", dinic(S, T, T+1));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: