所谓带下界的网络流和费用流,就是对于网络流和费用流模型的一些边,除了有容量之外,还有流量下界的限制,即这条边至少要流过下界这么多的流量。
在下文中,用x->y:[l,r]表示网络流模型中一条x连向y,容量为r,下界为l的边;用x->y:[l,r],c表示费用流模型中一条x连向y,容量为r,下界为l,费用为c的边。如果没有下界,则把[l,r]改为w。无穷大为INFI。源和汇分别为S和T。
1、带下界的可行流
这方面的论文好像有不少,做法大致有两种。一种是基于二分的,这种我还不清楚,有兴趣的自己去搜搜看吧……另一种就是重新构图的方法。
新建超级源S_和超级汇T_(本想叫S'和T'的,但发现看不清……)。连边:T->S:INFI。对于每条有下界的边x->y:[l,r],连边:S_->y:l、x->T_:l、x->y:r-l。求从S_到T_的最大流,如果S_的每条出边都满流,那么就存在可行流。
下面我们来说明这个构图方法的正确性。这个构图的思路是,强制让所有下界满流。如果我们直接让所有有下界的边的流量都达到了下界,这时候流量平衡条件会被破坏,那么我们就需要“补”一些流量。假设整个模型中只有一条边x->y:[l,r]有下界,那么我们让它的流量达到下界,此时y会多出l的流量需要流走,而x会有l的流量需要流入。注意我们是无法直接让这条边的流量达到下界的,那么我们就建超级源和超级汇来提供这些流量。所以就有S_->y:l、x->T_:l。但是这样y还是没法流到x,所以再连T->S:INFI,这样原来的源和汇就变成了满足流量平衡的普通节点。
我们可以这么理解:如果一条边x->y:[l,r]的下界可以被满足,那么从y到汇一定存在增广路可以流掉这l的流量,同样从源到x也一定存在这样的增广路。那么按照这个方法构图就可以求出是否存在这种增广路。
而为了保证下界不会被退流,原来x->y:[l,r]的边就被改成了x->y:r-l。
2、最大可行流
无论是求最大还是最小可行流,都是在求完可行流的残量网络上操作的。网络流模型的最大可行流即残量网络上原来的源到汇的最大流。
我们先不管T->S的边退回的流量。这样残量网络就是一个满足所有下界的网络,而且下界不会退流。那么在这个网络上求最大流得到的一定是除开下界产生的流量的的最大可行流。
那么下界的流量呢?事实上就是沿T->S流的流量,也就是T->S被退回的流量。如果可行流的流量没有经过T->S的边,那么就一定是流到了某个直接和T_相连的点,就直接流到T_了。在最初的模型中,也即从源出发的流量在流到汇之前经过了这两条边,也就只会计算一次流量。所以下界产生的流量实际上也就是T->S的流量。
这个方法是可以构造方案的。
3、最小可行流
网络流模型的最小可行流即T->S边的流量,减去,删去T->S的边后,从T到S的最大流。
为什么最小可行流不直接是T->S的流量?因为图中可能会存在从S到T再回到S的循环流。我们可以用管道的模型来比喻。一根管道的流量即每单位时间通过的水量,最大流也即源点每单位时间要供出去的水量。而对于一个循环流,我们只需在最开始的时候向源点供一点水,这点水就可以一直在循环的管道中流,而不需每单位时间都供水,所以这部分虽然有流量,但却是不应被计入最大流的部分。
这么说来好像平时求的一般向的最大流也不是最大流?事实上一般建出的网络流模型是不会存在循环流的,一般来说只有故意卡循环流的题目才会需要这样处理……
还要注意的一点是,T到S的最大流有可能比T->S的流量还要大,一减就变成负的流量了。而最大流是不能为负的。那么我们就再从S到T求一次最大流,但是注意只给S供应 恰好 T到S的最大流比T->S的流量 多出的流量(注意断句)。就相当于我们把源点视为一个普通的节点(不然就变成最大可行流了嘛……)。
这个方法也是可以构造方案的。
4、最小费用最大可行流
其实费用流在处理上和网络流是有一点。建模也是按照网络流的方法建(新建的边费用都为0,x->y:r-l的边费用为原费用),最小费用最大可行流就按照最大可行流的方法求,把求最大流改为求最小费用最大流就行了。
题目:
SGU173。给定网络,求能否使某些边满流,并求此时的最小可行流,并输出方案。
直接按照上面说的方法做就好了。代码:
//SGU176; Flow construction; Flow Network with Lower Bounds #include <cstdio> #include <cstdlib> #include <algorithm> #define N 300 #define M 20000 #define INFI 12345678 struct edge { int next, node, w; }e[M << 1 | 1]; struct inedge { int x, y, w, c; }ie[M + 1]; int head[N + 1], tot = 1; int n, m, x, y, w, c, S, T, S_, T_, ans, d[N + 1], q[N + 1], flow[M + 1], sum = 0; int in[N + 1], out[N + 1]; bool ok = true; inline void addedge(int a, int b, int w) { e[++tot].next = head[a]; head[a] = tot, e[tot].node = b, e[tot].w = w; e[++tot].next = head[b]; head[b] = tot, e[tot].node = a, e[tot].w = 0; } bool bfs(int S, int T) { int h = 0, t = 0; for (int i = 1; i <= N; ++i) d[i] = 0; d[S] = 1, q[t++] = S; while (h < t) { int cur = q[h++]; for (int i = head[cur]; i; i = e[i].next) { if (!e[i].w) continue; int node = e[i].node; if (d[node]) continue; d[node] = d[cur] + 1; q[t++] = node; } } return d[T]; } int dfs(int x, int inflow, int T) { if (x == T || !inflow) return inflow; int ret = inflow, flow; for (int i = head[x]; i; i = e[i].next) { int node = e[i].node; if (d[node] != d[x] + 1) continue; flow = dfs(node, std::min(e[i].w, ret), T); if (!flow) continue; e[i].w -= flow, e[i ^ 1].w += flow; ret -= flow; if (!ret) break; } if (ret == inflow) d[x] = -1; return inflow - ret; } inline int maxFlow(int S, int T) { int ret = 0; while (bfs(S, T)) ret += dfs(S, INFI, T); return ret; } int main() { #ifdef KANARI freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif scanf("%d%d", &n, &m); S = 1, T = n, S_ = n + 1, T_ = n + 2; for (int i = 1; i <= m; ++i) { scanf("%d%d%d%d", &x, &y, &w, &c); if (c) { addedge(x, y, 0); sum += w, in[y] += w, out[x] += w; } else addedge(x, y, w); ie[i].x = x, ie[i].y = y, ie[i].w = w, ie[i].c = c; } for (int i = 1; i <= n; ++i) { addedge(S_, i, in[i]); addedge(i, T_, out[i]); } addedge(T, S, INFI); if (!n) ok = false; if (sum != maxFlow(S_, T_)) ok = false; ans = e[head[T] ^ 1].w; e[head[T]].w = e[head[T] ^ 1].w = 0; ans -= maxFlow(T, S); if (ans < 0) { e[head[T]].w = e[head[T] ^ 1].w = 0; addedge(S_, S, -ans); maxFlow(S_, T); ans = 0; } for (int i = 1; i <= m; ++i) flow[i] = e[i * 2 ^ 1].w + ie[i].w * ie[i].c; if (!ok) printf("Impossible\n"); else { printf("%d\n", ans); for (int i = 1; i < m; ++i) printf("%d ", flow[i]); printf("%d\n", flow[m]); } return 0; }
class ArrayTransformations { public: #define N 250 string s; int a[N + 1], n, x; #define EDGE 10000 #define NODE 1000 #define INFI 123456789 struct edge { int next, node, w, c; }e[EDGE + 1]; int head[NODE + 1], tot; int d[NODE + 1], q[NODE + 1], f[NODE + 1], S, T, S_, T_, k, m, nodecnt; bool inq[NODE + 1]; inline void addedge(int a, int b, int w, int c, int w2 = 0) { e[++tot].next = head[a]; head[a] = tot, e[tot].node = b, e[tot].w = w, e[tot].c = c; e[++tot].next = head[b]; head[b] = tot, e[tot].node = a, e[tot].w = w2, e[tot].c = -c; // printf("%d %d %d %d\n", a, b, w, c); } inline int inc(int &x) { return x = x + 1 == NODE ? 0 : x + 1; } bool SPFA(int S, int T) { int h = 0, t = 0; for (int i = 0; i < nodecnt; ++i) d[i] = INFI, inq[i] = false; q[inc(t)] = S, inq[S] = true, d[S] = 0; while (h != t) { int cur = q[inc(h)]; inq[cur] = false; for (int i = head[cur]; i; i = e[i].next) { if (!e[i].w) continue; int node = e[i].node; if (d[node] > d[cur] + e[i].c) { d[node] = d[cur] + e[i].c; f[node] = i; if (!inq[node]) inq[node] = true, q[inc(t)] = node; } } } return d[T] != INFI; } inline int costFlow(int S, int T) { int w, ret = 0; while (SPFA(S, T)) { w = INFI; for (int x = T; x != S; x = e[f[x] ^ 1].node) w = std::min(w, e[f[x]].w); for (int x = T; x != S; x = e[f[x] ^ 1].node) e[f[x]].w -= w, e[f[x] ^ 1].w += w; ret += w * d[T]; } return ret; } int in[NODE + 1], out[NODE + 1]; inline bool check(int x) { int cost = 0; for (int i = 1; i <= n; ++i) if (a[i] > x) { cost += a[i] - x; if (a[i] - x > k) return false; } if (cost > m) return false; memset(head, 0, sizeof head); tot = 1; S = 0, T = n + 2; int SS = n + 3, TT = n + 4; S_ = n + 5, T_ = n + 6; nodecnt = n + 7; addedge(S, SS, k, 0); addedge(TT, T, k, 0); for (int i = 1; i <= n; ++i) addedge(SS, i, INFI, 0); for (int i = 2; i <= n + 1; ++i) addedge(i, TT, INFI, 0); memset(in, 0, sizeof in), memset(out, 0, sizeof out); for (int i = 1; i <= n; ++i) { if (a[i] > x) in[i + 1] += a[i] - x, out[i] += a[i] - x; addedge(i, i + 1, INFI, 1); } for (int i = 1; i <= n + 1; ++i) { addedge(S_, i, in[i], 0); addedge(i, T_, out[i], 0); } addedge(T, S, INFI, 0); cost += costFlow(S_, T_); return cost <= m; } int minimalValue(vector <string> initialArray, int K, int M) { k = K, m = M; s = ""; for (int i = 0; i < initialArray.size(); ++i) s += initialArray[i]; memset(a, 0, sizeof a); n = 0; stringstream ss(s); while (ss >> x) a[++n] = x; int l = 0, r = 0; for (int i = 1; i <= n; ++i) r = max(r, a[i]); while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } return l; } }