【模板】无源汇有上下界可行流(网络流)/ZOJ2314

1|0先导知识

网络最大流

2|0题目链接

https://vjudge.net/problem/ZOJ-2314

3|0题目大意

多组数据,第一行为数据组数 \(T\)
对于每一组数据,第一行为 \(n,m\) 表示 \(n\) 个结点,\(m\) 条有向边。
接下来 \(m\) 行,每一行有四个正整数 \(i,j,l_{ij},c_{ij}\) ,表示有一条从\(i\)\(j\)的有向边,要求正整数流量 \(f_{ij} \in [l_{ij},c_{ij}]\)
题目还提到,不存在自环;以及如果存在 \(i\)\(j\) 的边,则不存在 \(j\)\(i\) 的反向边。
如果有解输出 \(YES\) ,并按顺序输出每一条边的可行流 \(f_k\) ,否则输出 \(NO\)

4|0题目解析

题目为无源汇有上下界的可行流模板。
要点在于:
将每一条边用其 下界流 填满,算出每条边的剩余容量 \(cap_{ij} = c_{ij}-l_{ij}\) 以及每个结点的流量盈余 (\(R[x] = out_{\sum {l_{x*}}} - in_{\sum {l_{*x}}}\))。
新设置两个点——超级源点 \(s\) 和超级汇点 \(t\)
对于结点 \(x\),如果盈余为正,连接 \(s\rightarrow x\) ;如果盈余为负,连接 \(x\rightarrow t\) ,容量为\(|R[x]|\)。(这是本题的关键所在,为什么可以这样等效?)
对于新图,跑最大流即可。(本题解用的是\(ISAP\),用\(Dinic\)当然也可以啦。)
如果发现源点\(s\)出发的边不是满流(\(\exists i, cap_{si} > flow_{si}\)),则证明原图可行流不存在。
否则可以得到答案(\(f_{ij} = l_{ij} + flow_{ij}\))。

例如:
Sample Input

4 6
1 2 1 2
2 3 1 2
3 4 1 2
4 1 1 2
1 3 1 2
4 2 1 2

可以化作下图。

算出边的剩余容量 \(cap_{ij}\) (以黑色数字标注在边上)和结点的流量盈余 \(R[x]\) (标注在点上,红色为正,蓝色为负)。

然后连结超级源点 \(s\) 和超级汇点 \(t\)

通过这种方法可以转化成一般的最大流问题。

这样做为什么正确呢?

其实可以形象化地理解,设置源点 \(s\) 和汇点 \(t\) 是两个虚拟结点。

对于结点 \(x\),为了保证除了源汇两点以外的点流量守恒,即 \(out_{\sum {l_{x*}}} = in_{\sum {l_{*x}}}\)
如果 \(R[x]>0\) ,盈余为正,可以看作这部分的盈余是从源点 \(s\) 流入的,所以连接 \(s\rightarrow x\)
如果 \(R[x]<0\) ,发现“入不敷出”(盈余为负),可以看作这部分的亏损是流入汇点 \(t\) 导致的,所以连接 \(x\rightarrow t\)

最后跑完最大流,则检查这些虚拟的边是否“满流”。

如果不能满流,则说明盈余来自源点/亏损流向汇点的假设不能成立,存在无法满足流量守恒的结点,该网络图自然不存在可行流。
如果能够满流,则说明这样假设是能够成立的,网络中的流量就是可行流。
由于虚拟的源汇依然有出流=入流,因此只要检查从源点 \(s\) 流出的边(或者流出汇点 \(t\) 的边)是否满流即可。

5|0参考代码

#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 255; const int INF = 2147483647; struct Edge{ int from, to, cap, flow, min; }; int n, m, s, t, gap[N], cur[N], dep[N], R[N]; vector <Edge> e; vector <int> G[N]; void init() { memset(gap, 0, sizeof gap); memset(cur, 0, sizeof cur); memset(dep, 0, sizeof dep); ++gap[dep[t] = 1]; queue <int> Q; Q.push(t); while (!Q.empty()) { int x=Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i++) { int v = e[G[x][i]].to; if (!dep[v]) { ++gap[dep[v] = dep[x]+1]; Q.push(v); } } } } int augment(int x, int a) { if (x == t || !a) return a; int flow = 0; for (int &i=cur[x]; i < G[x].size(); i++) { Edge& b = e[G[x][i]]; if (dep[x] == dep[b.to] + 1 && b.cap > b.flow) { int tmp = augment(b.to, min(a, b.cap - b.flow)); flow += tmp; a -= tmp; b.flow += tmp; e[G[x][i]^1].flow -= tmp; if (!a) return flow; } } if (!(--gap[dep[x]])) dep[s] = n+1; ++gap[++dep[x]], cur[x] = 0; return flow; } ll maxFlow() { init(); ll ans = 0; while (dep[s] <= n) ans += augment(s, INF); for (int i = 0; i < G[s].size(); ++i) { if (e[G[s][i]].cap > e[G[s][i]].flow) return -1; } return ans; } void addEdge(int u, int v, int l, int c, int i) { e.push_back((Edge){u, v, c-l, 0, l}); e.push_back((Edge){v, u, 0, 0, 0}); G[u].push_back(i); G[v].push_back(i^1); R[u] -= l; R[v] += l; } void Clear() { e.clear(); for (int i = 0; i <= n; ++i) G[i].clear(); memset(R, 0, sizeof R); n = m = s = t = 0; } int main() { int T; scanf("%d", &T); for (int Kase=0; Kase<T; ++Kase) { if (Kase) {Clear(); putchar('\n');} scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) { int u, v, l ,c; scanf("%d%d%d%d", &u, &v, &l, &c); addEdge(u, v, l, c, i << 1); } s = ++n, t = ++n; int j = m-1; for (int i = 1; i <= n; ++i) { if (R[i] > 0) addEdge(s, i, 0, R[i], (++j) << 1); else if (R[i] < 0) addEdge(i, t, 0, -R[i], (++j) << 1); } ll a = maxFlow(); // printf("maxflow = %lld\n", a); // for (int i = 0; i <= j; ++i) { // printf("%d -> %d (%d/%d)\n", e[i << 1].from, e[i << 1].to, e[i << 1].flow, e[i << 1].cap); // } //这一部分查看可以看新图的流量情况 if (a == -1) {printf("NO\n"); continue;} printf("YES\n"); for (int i = 0; i < m; ++i) printf("%d\n", e[i << 1].flow+e[i << 1].min); } return 0; }

感谢支持!


__EOF__

本文作者炯炯目光
本文链接https://www.cnblogs.com/jjmg/p/13551520.html
关于博主:KTH 信息与网络工程硕士在读
版权声明:欢迎分享或转载
声援博主:To be or not to be, is a question.
posted @   Chiron-zy  阅读(320)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示