算法小站
首页
wiki
新鲜事
文章搜索
网站导航
回忆录
首页
wiki
新鲜事
文章搜索
网站导航
回忆录
登录
注册
算法基础
搜索
数据结构
字符串
数学
图论
动态规划
计算几何
杂项
wiki 主题
算法基础
搜索
数据结构
线段树
线段树单点修改
线段树区间修改
可持久化线段树
树链剖分
平衡树
FHQ-Treap
珂朵莉树
字符串
数学
图论
图的连通性
有向图的强连通分量
无向图的割点割边和双连通分量
流和匹配
最大流
最大流的一些应用
动态规划
计算几何
杂项
启发式合并
高精度
一些经典问题
约瑟夫环
主题名称
是否为父主题
是否为其添加wiki文章
取 消
确 定
选择主题
修改主题名称
修改父主题
修改wiki文章
取 消
删 除
修 改
算法基础
搜索
数据结构
字符串
数学
图论
动态规划
计算几何
杂项
图论
图的连通性
有向图的强连通分量
无向图的割点割边和双连通分量
流和匹配
最大流
最大流的一些应用
[[ item.c ]]
0
0
最大流和匹配
## 最大流问题 ### 流网络 一个源点和一个汇点,满足以下条件,边的容量为$$c(u, v)$$,流量为$$f(u, v)$$ 1)容量限制,$$0 \leqslant f(u, v) \leqslant c(u, v)$$ 2)流量守恒,除去源点和汇点,有$$\displaystyle \sum\_{v\in V} f(v, u) = \sum\_{u \in V} f(u, v)$$ 3)流定义为$$|f| = \sum\_{v \in V} f(s, v) - \sum\_{v \in V}f(v, s)$$ ### Ford-Fulkerson 方法 1)初始化流为$$f = 0$$ 2)**while** 如果在残量网络中存在一条增广路径$$p$$,沿着$$p$$来增加流$$f$$ 最后返回$$f$$ 残量网络形式化定义如下  ```math c_f(u, v) = \begin{cases} c(u, v) - f(u, v) & (u, v) \in E \\ f(v, u) & (v, u) \in E \\ 0 & \text{other} \end{cases} ``` 定义$$f \uparrow f'$$为流$$f'$$对$$f$$的递增,那么 ```math (f \uparrow f')(u, v) = \begin{cases} f(u, v) + f'(u, v) - f'(v, u) & (u, v) \in E \\ 0 & \text{other} \end{cases} ``` #### 引理 1 设$$G(V, E)$$是一个流网络,并且$$f$$是$$G$$的一个流,$$G_f$$是对应的残量网络,$$f'$$是$$G_f$$中的一个流 那么$$f \uparrow f'$$也是$$G$$中的一个流,并且$$|f \uparrow f'| = |f| + |f'|$$ > 证明如下 容量限制,$$f'(v, u) \leqslant c_f(v, u) = f(u, v)$$,从而 ```math (f \uparrow f')(u, v) = f(u, v) + f'(u, v) - f'(v, u) \geqslant f(u, v) + f'(u, v) - f(u, v) \\ = f'(u, v) \geqslant 0 ``` 同时还有 ```math (f \uparrow f')(u, v) = f(u, v) + f'(u, v) - f'(v, u) \leqslant f(u, v) + f'(u, v) \leqslant f(u, v) + c_f(u, v) \\ = f(u, v) + c(u, v) - f(u, v) = c(u, v) ``` 流量守恒,对于所有非源汇节点 ```math \displaystyle \sum_{v \in V} (f \uparrow f')(u, v) = \sum_{v \in V} ( f(u, v) + f'(u, v) - f'(v, u) ) \\ = \sum_{v \in V} f(u, v) + \sum_{v \in V} f'(v, u) - \sum_{v \in V} f'(u, v) \\ = \sum (f(v, u) + f'(v, u) - f'(u, v)) = \sum_{v \in V}(f \uparrow f')(v, u) ``` #### 引理 2 流量计算 不妨设$$V_1 = \\{v: (s, v) \in E\\}$$表示从源点$$s$$流出的流量,流量能到达的点的集合 令$$V_2 = \\{v: (v, s) \in E\\}$$表示流回源点$$s$$的流量,这些流量经过的点集 ```math \displaystyle |f \uparrow f'| = \sum_{v \in V_1}(f \uparrow f')(s, v) - \sum_{v \in V_2}(f \uparrow f')(v, s) \\ = \sum_{v \in V_1}(f(s, v) + f'(s, v) - f'(v, s)) - \sum_{v \in V_2} (f(v, s) + f'(v, s) - f'(s, v)) \\ = \sum_{v\in V_1} f(s, v) - \sum_{v \in V_2} f(v, s) + \left( \sum_{v \in V_1} f'(s, v) + \sum_{v \in V_2}f'(s, v) \right) - \left( \sum_{v \in V_1}f'(v, s) + \sum_{v \in V_2} f'(v, s) \right) \\ = \left( \sum_{v \in V_1}f(s, v) - \sum_{v \in V_2} f(v, s) \right) + \left( \sum_{v \in V_1 \cup V_2}f'(s, v) - \sum_{v \in V_1 \cup V_2} f'(v, s) \right) ``` 从而有$$|f \uparrow f'| = |f| + |f'|$$ ### 增广路径  沿着增广路径走,注意$$G_f$$**反向边流量增加** 一条增广路径$$p$$能够给$$G$$每条边增加的流量最大值,是路径$$p$$的**残存容量** ```math c_f(P) = \min c_f(u,v) \quad (u, v) \in P ``` 设$$G = (V, E)$$为一个流网络,$$f$$为$$G$$中的一个流,设$$p$$是残量网络$$G_f$$的一条增广路径 ```math f_p(u, v) = \begin{cases} c_f(p) & (u, v) \in p \\ 0 & \text{other}\end{cases} ``` ## 流网络的切割  切割$$(S, T)$$将流网络划分成$$S,T$$两个集合,$$T = V - S$$,其中满足$$s \in S, t \in T$$ **净流量** ```math \displaystyle f(S, T) = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{u \in S}\sum_{v \in T} f(v, u) ``` **割的容量** ```math \displaystyle c(S, T) = \sum_{u \in S} \sum_{v \in T} c(u, v) ``` **引理** 设$$f$$为流网络$$G$$中的一个流,$$(S, T)$$是流网络的任意切割,那么横跨切割$$(S, T)$$的**净流量**总相同,$$f(S, T) = |f|$$ > 证明如下 首先,根据**流量守恒** ```math \displaystyle u \in V - \{s, t\}: \quad \sum_{v \in V}f(u, v) - \sum_{v \in V}f(v, u) = 0 ``` 再对$$u \in S - \\{s\\}$$求和 ```math \displaystyle \sum_{u \in S - \{s\}} \left( \sum_{v \in V}f(u, v) - \sum_{v \in V} f(v, u) \right) = 0 ``` 根据$$|f|$$的定义 ```math \displaystyle |f| = \sum_{v \in V}f(s, v) - \sum_{v \in V}f(v, s) + \sum_{u \in S - \{s\}} \left( \sum_{v \in V}f(u, v) - \sum_{v \in V}f(v, u) \right) ``` 展开并且重新组合 ```math \displaystyle |f| = \sum_{v \in V} \left( f(s, v) + \sum_{u \in S - \{s\}} f(u, v) \right) - \sum_{v \in V} \left( f(v, s) + \sum_{u \in S - \{s\}} f(v, u) \right) \\ = \sum_{v \in V}\sum_{u \in S}f(u, v) - \sum_{v \in V}\sum_{u \in S}f(v, u) ``` 进一步,**拆点**,把$$v \in V$$拆成$$(v \in S) + (v \in T)$$ ```math \displaystyle |f| = \sum_{v \in S}\sum_{u \in S} f(u, v) + \sum_{v \in T}\sum_{u \in S}f(u, v) - \sum_{v \in S}\sum_{u \in S}f(v, u) - \sum_{v \in T}\sum_{u \in S}f(v, u) \\ = \sum_{v \in T}\sum_{u \in S}f(u, v) - \sum_{v \in T}\sum_{u \in S}f(v, u) + \left( \sum_{v \in S}\sum_{u \in S}f(u, v) - \sum_{v \in S}\sum_{u \in S}f(v, u) \right) ``` 注意到括号中的求和项是一样的 ```math \displaystyle |f| = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{u \in S} \sum_{v \in T} f(v, u) = f(S, T) ``` #### 推论 流网络$$G$$中的任意流$$f$$的值不能超过$$G$$的任意切割的**容量** ```math \displaystyle |f| = f(S, T) = \sum_{u \in S} \sum_{v \in T} f(u, v) - \sum_{u \in S} \sum_{v \in T} f(v, u) \\ \leqslant \sum_{u \in S} \sum_{v \in T} f(u, v) \leqslant \sum_{u \in S} \sum_{v \in T} c(u, v) = c(S, T) ``` **流网络中的最大流的值,实际上等于一个最小切割的容量** ## 最大流最小割定理 设$$f$$是流网络$$G = (V, E)$$中的一个流,源点是$$s$$,汇点是$$t$$,以下条件等价 1)$$f$$是$$G$$的一个最大流 2)残量网络$$G_f$$中不包括任何增广路径 3)$$|f| = c(S, T)$$,其中$$c(S, T)$$是流网络$$G$$的某个切割 > 证明如下 首先,$$(1) \Rightarrow (2)$$显然,因为如果有增广路径,$$|f| + |f_p|$$构成一个更大的流,与$$|f|$$是最大流矛盾 考虑$$(2) \Rightarrow (3)$$,残量网络$$G_f$$中不存在$$s \to t$$的路径,采用反证法 不妨令$$S = \\{v \in V: v \in G_f,\text{ 并且从 s 处可达} \\}, T = V - S$$ 显然我们有$$s \in S, t \notin S$$,划分$$(S, T)$$可以作为流网络$$G$$的一个切割 那么存在$$(u, v) \in E, (u, v) \notin E_f$$,也就是说$$c_f(u, v) = 0$$ 而我们根据残量网络的定义,对于$$(u, v) \in E$$,$$c_f(u, v) = c(u, v) - f(u, v)$$,那么$$c_f(u, v) = 0$$ 从而我们有$$f(u, v) = c(u, v), \Rightarrow f(v, u) = 0$$ 根据切割的流量定义,我们有 ```math \displaystyle f(S, T) = \sum_{u \in S}\sum_{v \in T}f(u, v) - \sum_{v \in T}\sum_{u \in S} f(v, u) = \sum_{u \in S}\sum_{v \in T}c(u, v) - \sum_{v \in T}\sum_{u \in S} 0 = c(S, T) ``` 证明完毕 最后,$$(3) \Rightarrow (1)$$显然 因为$$|f| \leqslant c(S, T)$$,$$|f| = c(S, T)$$取到最大值 ## 最大流一般算法 ### Ford-Fulkerson 算法 1)初始化$$|f| = 0$$ 2)**while** 在$$G_f$$中存在一条$$s \to t$$的增广路径$$p$$,那么 - 令$$c_f(p) = \min\\{c_f(u, v), \quad (u, v) \in p\\}$$ - **for** 所有$$(u, v) \in p$$ 如果是正向边,$$f(u, v) += c_f(p)$$,否则$$f(u, v) -= c_f(p)$$ 算法执行的时候,最多需要增广$$|f|$$次,复杂度是$$O(E|f|)$$ ### Edmonds-Karp 算法 1)定义每条边的权重为单位距离,$$\delta_f(u, v)$$表示残量网络$$G_f$$中$$s \to t$$的最短距离 2)沿着 **bfs** 找增广路径,选择$$s \to t$$的最短路作为增广路 > 时间复杂度分析 **引理** EK 算法运行在流网络$$G$$上,对于所有节点$$v \in V - \\{s, t\\}$$,残量网络$$G_f$$的最短路径$$\delta_f(s, v)$$ 随着流量递增而单调增加 证明如下 反证法,假设$$|f| < |f'|$$,但是对于边$$(u, v)$$,我们却有$$\delta\_f(s, u) \leqslant \delta\_{f'}(s, u), \ \delta\_f(s, v) > \delta\_{f'}(s, v)$$ 首先,对于$$(u, v) \in E\_{f'}$$,那么一定有$$(u, v) \notin E_f$$,否则的话 ```math \delta_f(s, v) \leqslant \delta_f(s, u) + 1 \leqslant \delta_{f'}(s, u) + 1 = \delta_f'(s, v) ``` 这与归纳假设矛盾 其次,$$(u, v) \in E\_f, (u, v) \notin E\_{f'}$$,这意味着操作增加了反向边$$v \to u$$的流量 即算法最后走的是$$E\_f(v, u)$$ ```math \delta_f(s, v) = \delta_f(s, u) - 1 \leqslant \delta_{f'}(s, u) - 1 = \delta_{f'}(s, v) - 2 ``` 这与$$\delta\_{f'}(s, v) < \delta\_f(s, v)$$矛盾 **定理** Edmonds-Karp 算法执行的流量增广操作总次数是$$O(VE)$$ 在残量网络$$G_f$$中,对于增广路径$$p$$,如果$$c_f(p) = c_f(u, v)$$,那么认为$$(u, v)$$是增广路径的关键边 对于$$|E|$$中的每条边而言,成为关键边的次数最多为$$|V|/2$$次  这里应用反向边这个概念,反向边就相当于我们**反悔,撤销之前的操作** 注意到,沿着增广路径增加流,关键边从残量网络$$G_f$$中消失,它什么时候再次成为关键边呢? 也就是说存在反悔行为,当$$(u, v)$$网络流量减小,$$(v, u)$$出现在增广路径上的时候 那么我们有 ```math \delta_f(s, v) = \delta_f(s, u) + 1 \\ \delta_{f'}(s, u) = \delta_{f'}(s, v) + 1 ``` 考虑一次增广,$$|f| < |f'|$$,我们有$$\delta\_{f}(s, v) \leqslant \delta\_{f'}(s, v)$$ ```math \delta_{f'}(s, u) = \delta_{f'}(s, v) + 1 = \delta_f(s, u) + 2 ``` 也就是说,一次增广,$$s \to ui$$的距离至少增加两个单位,而二者之间最大距离就是$$O(|V|)$$ 所以$$(u, v)$$能够成为关键边$$O(|V|/2)$$次,关键边总数是$$O(|E||V|/2)$$ ## Dinic 多路增广算法   ### Dinic 算法思想 1)沿着 bfs 最短路增广,通过 bfs 构造分层图,同时求出距离编号 注意,增广的时候,**有流量的正向边**我们才流过去,也就是说`if (e.flow and v 距离编号还没有求出来过)` 这个时候才进行增广,如果没有找到汇点,那么就意味着不存在增广路,`return false` 2)每找一轮,分层图可能因为反向边的加入,使得$$s \to t$$的距离变得更长,再继续找,直到没有增广路径 每找一轮,$$flow += \text{dfs}(s, +\infty)$$ ```bash while (bfs()) flow += dfs(s, numeric_limits<T>::max()) ``` #### dfs 多路增广 对于每个点$$u$$有一个流量上限,比如$$(u, \lim)$$,一开始的时候,起点流量无穷大,$$(s, +\infty)$$ 增广的过程大致如下  三个优化 1)当前弧优化,把增广完的分支及时删掉 2)流量上限优化,如果`lim = 0`,是不会再进行增广了,及时 break 3)零流优化,如果当前节点`flow = 0`,就说明流不出去了 之前我们分析,沿着 bfs 最短路增广,有势能$$(u, v): \delta_f(v) = \delta_f(u) + 1$$ 而流不出去了,需要把$$\delta = -1$$,`dis(u) = -1`表示之后不会往这里流了 主算法的实现并不复杂 1)从当前弧开始,分别考虑每一个分支 2)考虑能不能增广?充要条件有两个,$$e.flow > 0$$并且分层图上,$$\delta_v = \delta_u + 1$$ 然后`|f| = dfs(v, min(e.flow, lim))`, 增广`|f|`的流量 注意上面三个优化即可 ### dinic 算法实现 ```bash template<class T> class maxflow { public: struct _Edge { int to; T cap; _Edge(int to, T cap) : to(to), cap(cap) {} }; int _n; vector<_Edge> e; vector<vector<int> > g; vector<int> cur, dis; maxflow() {} maxflow(int n) { init(n); } void init(int n) { this->_n = n; e.clear(); g.assign(n+5, {}); cur.resize(n+5); dis.resize(n+5); } void addEdge(int u, int v, T cap) { g[u].push_back(e.size()); e.emplace_back(v, cap); g[v].push_back(e.size()); e.emplace_back(u, 0); } bool bfs(int s, int t) { dis.assign(_n+5, -1); cur.assign(_n+5, 0); queue<int> que; dis[s] = 0; que.push(s); while (!que.empty()) { const int u = que.front(); que.pop(); for (int i : g[u]) { auto [v, cap] = e[i]; if (cap > 0 && dis[v] == -1) { dis[v] = dis[u] + 1; if (v == t) return true; que.push(v); } } } return false; } T dfs(int u, int t, T flow) { if (u == t) return flow; auto rem = flow; for (int &i = cur[u]; i < (int)g[u].size(); i++) { const int j = g[u][i]; auto [v, cap] = e[j]; if (cap > 0 && dis[v] == dis[u] + 1) { auto f = dfs(v, t, min(rem, cap)); e[j].cap -= f; e[j ^ 1].cap += f; rem -= f; if (rem == 0) return flow; } } return flow - rem; } T dinic(int s, int t) { T flow = 0; while (bfs(s, t)) flow += dfs(s, t, numeric_limits<T>::max()); return flow; } struct Edge { int from, to; T cap, flow; }; vector<Edge> edges() { vector<Edge> a; for (int i = 0; i < e.size(); i += 2) { Edge x; x.from = e[i + 1].to, x.to = e[i].to; x.cap = e[i].cap + e[i + 1].cap; x.flow = e[i + 1].cap; a.push_back(x); } return a; } }; ```
看完文章有啥想法
发布评论
目录
[[ item.c ]]
12 人参与,0 条评论