题目链接: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\n", dinic(S, T, T+1)); 129 } 130 return 0; 131 }
时间: 2024-10-10 18:15:17