题中给出了各种点的定义,其实我们只要建立两个虚拟节点就可以了。一个是源点流到所有的p点,一个是汇点,所有的c点流过去,再求最大流就可以了。当然题中的输入数据比较恶心,这里用了字符串的处理方式来解决。
代码如下:
#include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> #include <queue> #define S N #define T (N+1) #define INF 0x3fffffff #define MAXN 105 using namespace std; int N, np, nc, M, flow[MAXN][MAXN], cap[MAXN][MAXN]; int c[MAXN], p[MAXN], Maxflow; void getint(int &t) { char c; while (c = getchar(), c < '0' || c > '9') {}; t = c - '0'; while (c = getchar(), c >= '0' && c <= '9') { t = t * 10 + c - '0'; } } void bfs() { bool finish = false; int pos; while (!finish) { memset(c, 0, sizeof (c)); c[S] = INF; // 虚拟出一个无穷的流从源点流出来 queue<int>q; q.push(S); while (!q.empty()) { if (c[T]) { break; } pos = q.front(); q.pop(); for (int i = 0; i <= N+1; ++i) { if (!c[i] && flow[pos][i] < cap[pos][i]) { // 前面这个!c[i]很重要,它表示本轮中推送过流量的点不能作为二次推流的节点 c[i] = min(cap[pos][i]-flow[pos][i], c[pos]); // 这个值一定是大于零的 p[i] = pos; // 记录路径,以便后面的更新 q.push(i); } } } if (c[T] == 0) { // 本次寻找增广路径失败 finish = true; } else { Maxflow += c[T]; // 流入到汇点的新流量 pos = T; while (pos != S) { // 没有回溯到源点 flow[p[pos]][pos] += c[T]; flow[pos][p[pos]] -= c[T]; pos = p[pos]; } } } } int main() { char s[15]; int x, y, z; // 从0号节点开始,我们可以设置一个N号节点,并建立c到N节点的流量无上限的边 while (scanf("%d %d %d %d", &N, &np, &nc, &M) == 4) { memset(cap, 0, sizeof (cap)); memset(flow, 0, sizeof (flow)); Maxflow = 0; for (int i = 0; i < M; ++i) { getint(x), getint(y), getint(z); // printf("%d %d %d\n", x, y, z); cap[x][y] = z; // 建立边的容量 } for (int i = 0; i < np; ++i) { getint(x), getint(y); cap[N][x] = y; // 创建源点 } for (int i = 0; i < nc; ++i) { getint(x), getint(y); cap[x][N+1] = y; // 创建汇点 } bfs(); printf("%d\n", Maxflow); } return 0; }