POJ-1149 PIGS 网络流
题意:一个农夫要把养殖的猪卖出去,现有M个猪圈,农夫自己没有猪圈的钥匙。现有N个客户要来买猪,每个客户手中有Ai把钥匙,分别表示成猪圈的编号,并且每个客户需要买一定的猪。这些客户依次过来(编号从小到大),现在农夫可以选择如何将这些猪卖给每一个客户,只要头数不超过需求即可。农夫在处理好每一笔交易后能够将打开的猪圈门之内的猪任意分配。现在问农夫能够卖出最多的猪的数量。
解法:通过这一题,发现构边的时候其实是有很多技巧的,这题如果把猪圈放到图中的话那么将成为一道不可做题。原因是时间复杂度和空间复杂度太高了。其实可以画出一个简图,然后再通过合并边和合并点来简化构图。由于农夫能够在打开猪圈后任意分配猪。因此如果第j个客户能够买第i个客户买过的猪圈中的猪,那么就可以视作第j个客户代替i先买。也就是说这里有一个边的合并过程。如果每个人都有这种代理的想法,那么农夫(相当于源点)就只要提供猪给第一个打开某个猪圈的客户。因此最后的构边过程可以描述如下:
1.第i个客户如果无法找到有代理人的猪圈,那么自己成为当前猪圈的代理人,从源点连一条边到i,容量为猪圈中猪的总数;
2.如果该号猪圈有代理人,那么这个猪圈的代理人 j(注意并不止一个)连一条边到客户 i,容量无穷大,i 也成为该猪圈的代理人。注意这里有一个优化就是只要从最近的代理人那里连一条边到 i 即可,因为其他代理人的边可以通过最近的代理人传递过来。
3.每个客户连一条到汇点,容量为买猪的数量。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; int M, N; const int SS = 0, TT = 103; const int INF = 0x3f3f3f3f; // M个猪圈和N个顾客 struct Edge { int v, c, next; }e[20005]; int cap[1005], link[1005]; int idx, head[105]; int que[105]; int level[105]; // 对每个顶点分一个层 int front, tail; void insert(int a, int b, int c) { e[idx].v = b, e[idx].c = c; e[idx].next = head[a]; head[a] = idx++; } bool bfs() { front = tail = 0; que[tail++] = SS; memset(level, 0xff, sizeof (level)); level[SS] = 0; while (front != tail) { int u = que[front++]; for (int i = head[u]; i != -1; i = e[i].next) { if (!(~level[e[i].v]) && e[i].c) { level[e[i].v] = level[u] + 1; if (e[i].v == TT) return true; que[tail++] = e[i].v; } } } return ~level[TT]; } int dfs(int u, int sup) { if (u == TT) return sup; int tf = 0, f; for (int i = head[u]; i != -1; i = e[i].next) { if (level[u]+1 == level[e[i].v] && e[i].c && (f = dfs(e[i].v, min(e[i].c, sup-tf)))) { tf += f; e[i].c -= f, e[i^1].c += f; if (tf == sup) return sup; } } if (!tf) level[u] = -1; return tf; } int dinic() { int ret = 0; while (bfs()) { ret += dfs(SS, INF); } return ret; } int main() { while (scanf("%d %d", &M, &N) != EOF) { int x, y, tot; idx = 0; memset(head, 0xff, sizeof (head)); for (int i = 1; i <= M; ++i) { link[i] = -1; scanf("%d", &cap[i]); } for (int i = 1; i <= N; ++i) { tot = 0; scanf("%d", &x); for (int j = 1; j <= x; ++j) { scanf("%d", &y); if (~link[y]) { // 如果这个节点已经在其他客户出现 insert(link[y], i, INF); insert(i, link[y], 0); } else { tot += cap[y]; } link[y] = i; } if (tot) { insert(SS, i, tot); insert(i, SS, 0); } scanf("%d", &y); insert(i, TT, y); insert(TT, i, 0); } printf("%d\n", dinic()); } return 0; }