SGU 176.Flow construction (有上下界的最大流)
时间限制:0.5s
空间限制:4M
题意:
有一个由管道组成的网络,有n个节点(n不大于100),1号节点可以制造原料,最后汇集到n号节点。原料通过管道运输。其中有一些节点有管道连接,这些管道都有着最大的流量限制,其中有一些管道必须充满。求1号节点最小的制造原料速度。如果原料不能运输到n,输出“Impossible”
Solution
第一道有上下界的网络流。从理解不太深刻,wa了很多次后。完全理解了有上下界网络流的算法。
首先,要构造一个伴随网络,由此判断,是否能让所有的下界满足,并连通源汇点。
由于是求最小流,我们需要知道,是否能从汇点找到一条到源点的增广路。使得最大流减小。
这时可能求出负流flow,只要新加一个节点0,添加到汇点的一条容量为-flow的边,即可让负流变为0;
具体的构造方法在程序注释里。
/* 有容量上下界的最大流算法 1)cap(u,v)为u到v的边的容量 2)Gup(u,v)为u到v的边流量上界 3)Glow(u,v)为u到v的边流量下界 4)st(u)代表点u的所有出边的下界之和 5)ed(u)代表点u的所有入边的下界之和 6)S为源点,T为汇点 新网络D的构造方法: 1)加入虚拟源点SS,ST 2)如果边(u,v)的容量cap(u,v)=Gup(u,v)-Glow(u,v) 3)对于每个点v,加入边(SS,v)=ed(v); 4)对于每个点u,加入边(u,ST)=st(u); 5)cap(T,S)=+∞; 6)tflow为所有边的下界之和 求SS到ST的最大流,若最大流不等于tflow,则不存在可行流,此问题无解。 在新网络D中去掉所有与SS,ST相连的边。求最大流。 最后将两个流值相加 最小流,第二次最大流从T到S运行。 */ #include <iostream> #include <cstdio> #include <cstring> #define ms(a,b) memset(a,b,sizeof a) using namespace std; const int INF = 111; struct node { int u, v, c, next; } edge[INF * INF << 2]; int Gup[INF][INF], Glow[INF][INF], st[INF], ed[INF], cap[INF][INF], tflow; int pHead[INF*INF], SS, ST, S, T, nCnt, ans; //同时添加弧和反向边, 反向边初始容量为0 void addEdge (int u, int v, int c) { edge[++nCnt].v = v, edge[nCnt].u = u, edge[nCnt].c = c; edge[nCnt].next = pHead[u]; pHead[u] = nCnt; edge[++nCnt].v = u, edge[nCnt].u = v, edge[nCnt].c = 0; edge[nCnt].next = pHead[v]; pHead[v] = nCnt; } int SAP (int pStart, int pEnd, int N) { int numh[INF], h[INF], curEdge[INF], pre[INF]; int cur_flow, flow_ans = 0, u, neck, i, tmp; ms (h, 0); ms (numh, 0); ms (pre, -1); for (i = 0; i <= N; i++) curEdge[i] = pHead[i]; numh[0] = N; u = pStart; while (h[pStart] <= N) { if (u == pEnd) { cur_flow = 1e9; for (i = pStart; i != pEnd; i = edge[curEdge[i]].v) if (cur_flow > edge[curEdge[i]].c) neck = i, cur_flow = edge[curEdge[i]].c; for (i = pStart; i != pEnd; i = edge[curEdge[i]].v) { tmp = curEdge[i]; edge[tmp].c -= cur_flow, edge[tmp ^ 1].c += cur_flow; } flow_ans += cur_flow; u = neck; } for ( i = curEdge[u]; i != 0; i = edge[i].next) { if (edge[i].v > N) continue; //重要!!! if (edge[i].c && h[u] == h[edge[i].v] + 1) break; } if (i != 0) { curEdge[u] = i, pre[edge[i].v] = u; u = edge[i].v; } else { if (0 == --numh[h[u]]) continue; curEdge[u] = pHead[u]; for (tmp = N, i = pHead[u]; i != 0; i = edge[i].next) { if (edge[i].v > N) continue; //重要!!! if (edge[i].c) tmp = min (tmp, h[edge[i].v]); } h[u] = tmp + 1; ++numh[h[u]]; if (u != pStart) u = pre[u]; } } return flow_ans; } int solve (int n) { //建立伴随网络 SS = n + 1, ST = n + 2; for (int i = 1; i <= n; i++) { if (ed[i]) addEdge (SS, i, ed[i]); if (st[i]) addEdge (i, ST, st[i]); } //T到S添加一条无限容量边 addEdge (T, S, 0x7ffffff); //判断可行流 int tem = SAP (SS, ST, ST); if (tem != tflow) return -1; else { edge[nCnt].c = edge[nCnt - 1].c = 0; //删除S到T的无限容量边 int kkk = SAP (T, S, T); return 1; } } int n, m, x, y, c, sta; int main() { /* 建图,前向星存边,表头在pHead[],边计数 nCnt. S,T分别为源点和汇点 */ scanf ("%d %d", &n, &m); nCnt = 1; for (int i = 1; i <= m; i++) { scanf ("%d %d %d %d", &x, &y, &c, &sta); Gup[x][y] = c; if (sta) { Glow[x][y] = c; st[x] += c, ed[y] += c; tflow += c; } addEdge (x, y, Gup[x][y] - Glow[x][y]); } S = 1, T = n; ans = 0; if (solve (n) > 0) { for (int i = 2; i <= nCnt; i += 2) { if (edge[i].v <= T && edge[i].u == 1) ans += Gup[edge[i].u][edge[i].v] - edge[i].c; if (edge[i].u <= T && edge[i].v == 1) ans -= Gup[edge[i].u][edge[i].v] - edge[i].c; } if (ans < 0) { S = 0; addEdge (S, 1, -ans); ans = 0; SAP (S, T, T); } printf ("%d\n", ans); for (int i = 2; i <= 2 * m; i += 2) printf ("%d ", Gup[edge[i].u][edge[i].v] - edge[i].c); } else puts ("Impossible"); return 0; }