费用流

做法

  1. 在残余网络上寻找最短路
  2. 对该路径进行增广, 对答案产生贡献
  3. 不断重复opt.1操作, 直至 s → t s\to t s→t不存在路径

证明

定义

  1. 定义 f f f作为图中的流

  2. f - g f\text{-}g f-g表示流 f f f与流 g g g之间不同的流量

  3. i n ( u ) in(u) in(u)表示 u u u的入流, o u t ( u ) out(u) out(u)表示 u u u的出流

Proof 1

  • f f f是最小费用流 ⇔ \Leftrightarrow ⇔ 残余网络中无负圈

假设, 存在费用比 f f f更小的流 f ′ f' f′.

观察二者, 由于流量相同, 那么 o u t ( s ) , i n ( t ) out(s), in(t) out(s),in(t)均相同

且 ∀ u , i n ( u ) = o u t ( u ) \forall u, in(u)=out(u) ∀u,in(u)=out(u) 在 f , f ′ f, f' f,f′中恒成立.

于是 f ′ - f f'\text{-}f f′-f形成的流是由若干圈组成的!

因为 c o s t ( f ′ ) &lt; c o s t ( f ) cost(f') &lt; cost(f) cost(f′)<cost(f) 故$f $的残留网络中存在至少一个负圈.

Proof 2

  • 利用数学归纳证明

假设 f i f_i fi​ 表示流量为 i i i的最小费用流

f 0 f_0 f0​便是原图(显然原图中不存在负圈).

那么根据我们的做法, 得到了 f i + 1 f_{i+1} fi+1​, 那么假设存在费用更小的流 f i + 1 ′ f_{i+1}' fi+1′​

则 f i + 1 - f i f_{i + 1} \text{-}f_i fi+1​-fi​是一条 s → t s\to t s→t的最短路, 而 f i + 1 ′ - f i f_{i+1}'\text{-}f_i fi+1′​-fi​是一条 s → t s\to t s→t的路径与若干圈组成的

那么这些圈中则必定存在负圈, 这与 f i f_i fi​是最小费用流相悖.

故上述成立.


Dijksta 优化费用流

不要在意名字

背景: 图中存在负权边, spfa已经死了

思考: 求最短路可否用Dijkstra呢?

假如我们给每一个节点 附上 h ( i ) h(i) h(i):使得 e ( u , v ) ′ = e ( u , v ) + h ( u ) − h ( v ) e(u,v)' = e(u,v) + h(u) - h(v) e(u,v)′=e(u,v)+h(u)−h(v)

且它恒非负, 那就可以用 Dijkstra \text{Dijkstra} Dijkstra了

  • 考虑最短路的性质: d i s ( u ) + e ( u , v ) ≥ d i s ( v ) dis(u) + e(u, v) \geq dis(v) dis(u)+e(u,v)≥dis(v)

得到 d i s ( u ) − d i s ( v ) + e ( u , v ) ≥ 0 dis(u)-dis(v) + e(u, v) \geq 0 dis(u)−dis(v)+e(u,v)≥0

那么我们将 d i s ( u ) − d i s ( v ) + e ( u , v ) dis(u)-dis(v) + e(u, v) dis(u)−dis(v)+e(u,v)作为新的边权, 记为 e ( u , v ) ′ e(u,v)' e(u,v)′

不难证明以它为新图所得到的最短路与原图的最短路经过路径是一样的.

那么这样就意味着我们可以 Dijkstra \text{Dijkstra} Dijkstra了. (比spfa不知道高到哪里去了, 雾

即: 在跑no.i次增广的时候的势 h i ( u ) h_i(u) hi​(u)为 d i s i ( u ) dis_i(u) disi​(u)

等等, 如果我们已经知道了势(即最短路), 那还tm要增广干嘛

于是发现其实 h i ( u ) = h i − 1 ( u ) h_i(u)=h_{i-1}(u) hi​(u)=hi−1​(u)也是可以的, 即变成上次增广的原图中 u u u的最短路.

从简证明:

  1. 若e(u,v)在no.i-1次增广时存在, 那么显然满足

  2. 若e(u,v)在no.i-1次增广不存在

    那么此次它的出现是因为增广导致的

    意思就是说它一定在上次的 s → t s\to t s→t的最短路上, 那么e(u, v) = -e(v, u) = 0

    依旧非负.

%:pragma GCC optimize("Ofast", 2)
#include <bits/stdc++.h>
using namespace std;namespace {inline void read(int &x) {x = 0; int f = 1; char c = getchar();for(; !isdigit(c); c = getchar())if(c == '-') f = -1;for(;  isdigit(c); c = getchar())x = (x << 3) + (x << 1) + (c ^ '0');x *= f;}
}const int N = 5e3 + 5, M = 5e4 + 5;
const int inf = 1e9;# define pi pair<int, int>int n, m, s, t, u, v, c, w;namespace Primal {int Ecnt = 1, first[N], nex[M * 2], arr[M * 2], cap[M * 2], cost[M * 2];int dis[N], h[N], pree[N], prev[N], F, C;template <typename T>inline void Min(T &a, T b) {if(a > b) a = b;}inline void Ad(int u, int v, int c, int w) {nex[++Ecnt] = first[u], first[u] = Ecnt, arr[Ecnt] = v, cap[Ecnt] = c, cost[Ecnt] = w;}inline void add(int u, int v, int c, int w) {Ad(u, v, c, w), Ad(v, u, 0, -w);}void Dijkstra() {static priority_queue<pi, vector<pi>, greater<pi> > q;for(; !q.empty(); q.pop());fill(dis, dis + 1 + n, -1);dis[s] = 0, q.push(pi(0, s));// printf("-----------\n");while(!q.empty()) {pi now = q.top(); q.pop();int u = now.second;if(dis[u] < now.first) continue;for(int i = first[u]; i; i = nex[i]) {static int v; v = arr[i];if(!cap[i]) continue;if(dis[v] < 0 || dis[v] > dis[u] + cost[i] + h[u] - h[v]) {dis[v] = dis[u] + cost[i] + h[u] - h[v];prev[v] = u, pree[v] = i;q.push(pi(dis[v], v));}}}}pi solve(int s, int t) {fill(h, h + 1 + n, 0);for(int f = inf; f > 0; ) {Dijkstra();if(dis[t] < 0) break;for(register int i = 1; i <= n; ++i) // be careful this forh[i] += (dis[i] != -1) ? dis[i] : 0;int d = f;for(int u = t; u != s; u = prev[u]) Min(d, cap[pree[u]]);f -= d, F += d, C += h[t] * d;assert(C >= 0);for(int u = t; u != s; u = prev[u]) {cap[pree[u]] -= d;cap[pree[u] ^ 1] += d;}} return pi(F, C);}
}
using namespace Primal;int main() {read(n), read(m), read(s), read(t);for(int i = 1; i <= m; ++i) {read(u), read(v), read(c), read(w);add(u, v, c, w);}pi get = solve(s, t);printf("%d %d\n", get.first, get.second);return 0;
}

其实大家可能最疑惑的就是为什么有代码是 :

   for(int i = 1; i <= n; ++i) h[i] += dist[i];

从理论出发, h ′ ( i ) h'(i) h′(i)此时定义为no.(i-1)次增广时原图中的最短路 (再次强调是原图!)

而数组dist实际存储的是

dist[u] = ∑ e ′ ( u , v ) = ∑ ( h ( u ) − h ( v ) + e ( u , v ) ) = d i s ( u ) + h ( s ) − h ( u ) = d i s ( u ) − h ( u ) \begin{aligned} \text{dist[u]}&amp;=\sum e'(u, v) \\ &amp;=\sum \bigg( h(u)-h(v) + e(u, v) \bigg) \\ &amp;= dis(u) + h(s)-h(u) \\ &amp;= dis(u) - h(u) \\ \end{aligned} dist[u]​=∑e′(u,v)=∑(h(u)−h(v)+e(u,v))=dis(u)+h(s)−h(u)=dis(u)−h(u)​

那么 h ′ ( u ) = d i s ( u ) = h ( u ) + d i s t [ u ] ​ h'(u) = dis(u) = h(u) + dist[u]​ h′(u)=dis(u)=h(u)+dist[u]​

所以就是 一直都是 “+=”

最小费用最大流(详解+模板)相关推荐

  1. 最小费用最大流背诵用模板

    这是我纯自己打的,洛谷上竟然A了 不敢相信. 丑陋的代码: #include<iostream> #include<cstdio> using namespace std; # ...

  2. 有源汇上下界最小费用可行流 ---- P4043 [AHOI2014/JSOI2014]支线剧情(模板)

    题目链接 题目大意: 解题思路: 有源汇上下界最小费用可行流模板题目来着 先建出一个有源汇上下界可行流的图,然后注意建图的时候要把每条边的下界的费用提前加到ans里面 然后再对图跑费用流,就是补齐费用 ...

  3. Doctor NiGONiGO’s multi-core CPU(最小费用最大流模板)

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=693 题意:有一个 k 核的处理器和 n 个工作,全部的工作都须要在一个核上处理一个单位的 ...

  4. 费用流:最大费用最大流和最小费用最大流(模板)

    主要是思维建边,建有向边,然后跑模板就行了 可以解决KM算法所能解决的问题(完全取代) 可以解决非完备匹配问题中的最大权匹配和最小权匹配,分别对应着最大费用最大流和最小费用最大流 模板: 最大费用最大 ...

  5. 经典网络流题目模板(P3376 + P2756 + P3381 : 最大流 + 二分图匹配 + 最小费用最大流)...

    题目来源 P3376 [模板]网络最大流 P2756 飞行员配对方案问题 P3381 [模板]最小费用最大流 最大流 最大流问题是网络流的经典类型之一,用处广泛,个人认为网络流问题最具特点的操作就是建 ...

  6. 最大流、最小费用最大流【模板】

    一下代码版权归:HIT    xiaodai 最大流模板:(题目链接) #include <cstring> #include <algorithm> #include < ...

  7. 最小费用最大流 【模板】

    如果理解了最大流连续增广路算法的思维, 理解这个算法还是很简单的. 结构体存储信息: 分别为边的起点.终点.容量.当前流量.费用.下一条边的编号. struct Edge {int from, to, ...

  8. 最大流 最小费用最大流模板

    模板从  这里   搬运,链接博客还有很多网络流题集题解参考. 最大流模板 ( 可处理重边 ) const int maxn = 1e6 + 10; const int INF = 0x3f3f3f3 ...

  9. 乌鲁木齐网络赛J题(最小费用最大流模板)

    ACM ICPC 乌鲁木齐网络赛 J. Our Journey of Dalian Ends 2017-09-09 17:24 243人阅读 评论(0) 收藏 举报  分类: 网络流(33)  版权声 ...

最新文章

  1. 3-2 -------WERTYU------题挺水的
  2. 将选定的文本对象左对齐、右对齐或对中
  3. .NET经典资源站点汇总
  4. 如何隐藏iPhone导航栏上的“后退”按钮?
  5. 手把手带你领略双十一背后的核心技术Sentinel之服务的熔断降级
  6. ajax的常见几种写法以及用法
  7. 第三次课堂总结--付胤
  8. 如何设计更好的脉搏血氧仪:实施
  9. 【JAVA】JSON随机值生成工具类
  10. python 基于numpy的线性代数运算
  11. python迭代法求解方程_第一部分:趣味算法入门;第六题牛顿迭代法求一元三次方程的根...
  12. 行业首选|墨菲安全实力入选《开发安全产品及服务购买决策参考》
  13. python提取图片文字
  14. 深度学习AI美颜系列---人像分割头发细节处理算法研究
  15. 为什么需要API接口开发?
  16. r语言ggplot2一夜多图_关于GGPLOT2出图里的一页多图模式
  17. 公共钥匙盒 Java算法
  18. atch: shared pool 优化探索
  19. Retrofit2学习项目_2
  20. 【NLP】文本关键词提取的两种方法-TFIDF和TextRank

热门文章

  1. “以简驭繁”打造卓越校园网 ——锐捷助力西安理工大学校园网建设
  2. JavaScript|表格隔行变色(可作模板copy)
  3. OFDMA trigger frame
  4. RGB565 RGB888
  5. 英孚教育APP应用获全球杰出商业银奖
  6. 三次握手四次挥手详解
  7. python3 将科学计数法(用E表示的)转化为书写形式的科学计数法的数字
  8. Error LNK1120: 1 个无法解析的外部命令
  9. 文本相似度的那些算法
  10. Element表格内容不垂直居中和内容换行显示