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

  题目链接: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板子,代码如下:

  1 #include <algorithm>
  2 #include <iostream>
  3 #include <iomanip>
  4 #include <cstring>
  5 #include <climits>
  6 #include <complex>
  7 #include <fstream>
  8 #include <cassert>
  9 #include <cstdio>
 10 #include <bitset>
 11 #include <vector>
 12 #include <deque>
 13 #include <queue>
 14 #include <stack>
 15 #include <ctime>
 16 #include <set>
 17 #include <map>
 18 #include <cmath>
 19 
 20 using namespace std;
 21 
 22 typedef struct Edge {
 23     int u, v, c, next;
 24 }Edge;
 25 
 26 const int inf = 0x7f7f7f7f;
 27 const int maxn = 2222;
 28 
 29 int cnt, head[maxn];
 30 int cur[maxn], dd[maxn];
 31 Edge edge[maxn<<1];
 32 
 33 int N, F, D;
 34 int n[maxn], f[maxn], d[maxn];
 35 int S, T;
 36 
 37 void init() {
 38     memset(head, -1, sizeof(head));
 39     for(int i = 0; i < maxn; i++) edge[i].next = -1;
 40     S = 0;
 41 }
 42 
 43 void adde(int u, int v, int c, int c1) {
 44     edge[cnt].u = u; edge[cnt].v = v; edge[cnt].c = c; 
 45     edge[cnt].next = head[u]; head[u] = cnt++;
 46     edge[cnt].u = v; edge[cnt].v = u; edge[cnt].c = c1; 
 47     edge[cnt].next = head[v]; head[v] = cnt++;
 48 }
 49 
 50 bool bfs(int s, int t, int n) {
 51     queue<int> q;
 52     for(int i = 0; i < n; i++) dd[i] = inf;
 53     dd[s] = 0;
 54     q.push(s);
 55     while(!q.empty()) {
 56         int u = q.front(); q.pop();
 57         for(int i = head[u]; ~i; i = edge[i].next) {
 58             if(dd[edge[i].v] > dd[u] + 1 && edge[i].c > 0) {
 59                 dd[edge[i].v] = dd[u] + 1;
 60                 if(edge[i].v == t) return 1;
 61                 q.push(edge[i].v);
 62             }
 63         }
 64     }
 65     return 0;
 66 }
 67 
 68 int dinic(int s, int t, int n) {
 69     int st[maxn], top;
 70     int u;
 71     int flow = 0;
 72     while(bfs(s, t, n)) {
 73         for(int i = 0; i < n; i++) cur[i] = head[i];
 74         u = s; top = 0;
 75         while(cur[s] != -1) {
 76             if(u == t) {
 77                 int tp = inf;
 78                 for(int i = top - 1; i >= 0; i--) {
 79                     tp = min(tp, edge[st[i]].c);
 80                 }
 81                 flow += tp;
 82                 for(int i = top - 1; i >= 0; i--) {
 83                     edge[st[i]].c -= tp;
 84                     edge[st[i] ^ 1].c += tp;
 85                     if(edge[st[i]].c == 0) top = i;
 86                 }
 87                 u = edge[st[top]].u;
 88             }
 89             else if(cur[u] != -1 && edge[cur[u]].c > 0 && dd[u] + 1 == dd[edge[cur[u]].v]) {
 90                 st[top++] = cur[u];
 91                 u = edge[cur[u]].v;
 92             }
 93             else {
 94                 while(u != s && cur[u] == -1) {
 95                     u = edge[st[--top]].u;
 96                 }
 97                 cur[u] = edge[cur[u]].next;
 98             }
 99         }
100     }
101     return flow;
102 }
103 
104 int main() {
105     // freopen("in", "r", stdin);
106     while(~scanf("%d %d %d", &N, &F, &D)) {
107         init();
108         T = 2 * N + F + D + 1;
109         int ff, dd;
110         //把牛的位置拆开,放在食物和饮料中间
111         //S指向食物,饮料指向T,牛指向牛
112         for(int i = 1; i <= F; i++) adde(S, i, 1, 0);
113         for(int i = 1; i <= D; i++) adde(2*N+F+i, T, 1, 0);
114         for(int i = 1; i <= N; i++) adde(F+i, F+N+i, 1, 0);
115         //确定食物指向牛、牛指向饮料的关系
116         for(int i = 1; i <= N; i++) {
117             int tmp;
118             scanf("%d %d", &ff, &dd);
119             for(int j = 0; j < ff; j++) {
120                 scanf("%d", &tmp);
121                 adde(tmp, F+i, 1, 0);
122             }
123             for(int j = 0; j < dd; j++) {
124                 scanf("%d", &tmp);
125                 adde(F+N+i, 2*N+F+tmp, 1, 0);
126             }
127         }
128         printf("%d
", dinic(S, T, T+1));
129     }
130     return 0;
131 }
原文地址:https://www.cnblogs.com/kirai/p/4963293.html