new_hope网络流
网络流与费用流
——汪凌岳
内容:
- 流网络
- 最大流问题与解决思想、实现算法
- 习题1-2
- 费用流
- 习题4
- 文献参考
tips:
建议听的时候把一些关键函数名,函数之间的关系简记下来。
一、流网络
1.形式化定义:
设 \(G = (V,E)\) 为一个流网络,\(s\),\(t\) 分别为网络的源节点和汇点,容量函数 \(c\),流函数 \(f\),满足以下性质:
- 容量限制:\(\forall u, v \in V, 0 \le f(u,v) \le c(u,v)\),当 \((u,v) \notin E\) 时,\(f(u,v) = 0\)
- 流量守恒:\(\forall u \in V - \{ s, t\}, \sum_{v \in V} f(v,u) = \sum_{v \in V} f(u,v)\)
wuhupai注:前一个\(\sum\)的意思就是所有流向u的东西,后一个\(\sum\)说的是从u流出去的所有东西,这显然是对的
值得注意的是,若流网络的边集 \(E\) 包含一条边 \((u,v)\),则不存在反方向的边 \((v,u)\),所以对于流量守恒这一性质的理解应是,对于一个点 \(u\),流入量等于流出量,那么对于整张网络而言,便有全局流入量等于全局流出量,而并非正反两条边流量相等。(形式化的定义容易带偏理解)
我们定义流网络 \(G\) 的一个流 \(f\) 的值 \(|f|\) 如下:
即流入源节点 \(s\) 的流之和 - 流出源节点 \(s\) 的流之和,通常情况下,右边的求和项为 \(0\)(没有流入 \(s\) 的流量),而这个完整的式子接下来会用到。
2.两个简单的转化:
- 反平行边:
流网络中,若 \((u,v) \in E\),则 \((v,u) \notin E\),但实际情况中,若图同时存在 \((u, v)\) 和 \((v,u)\),则称这两条边为反平行边,我们可以通过分裂其中一条反平行边,加入新节点,从而得到与原图等价的网络【画图说明】。(这里不能简单等价为一条容量为 \(c(u,v) - c(v,u)\) 的边) - 超级源点与超级汇点:
若一张网络有多个源点与多个汇点,则可通过加入超级源点与超级汇点,设容量为正无穷,显然地,这样得到的网络与原图等价。
3.思考题
假定除边的容量外,流网络还有节点容量,记为 \(l(v)\),则如何构造除一个带有节点容量的流网络 \(G'\) 转化为一个等价的不带有节点容量的流网络 \(G\)。
\({拆点,加边} 【画图说明】\)
二、最大流问题与解决思想、实现算法
1. 概念引入:
- 残存网络
设残存网络 \(G_f\),\(G_f\) 由仍有空间对流量 \(f\) 进行调整的边构成。但同时,为了试探更大的流量,可能会需要有对正流量 \(f(u,v)\) 的缩减,此时则需要引入原图 \(G\) 中没有的边,利用反向流量抵消正向流量,从而达到缩减。
因此,我们形式化地将残存网络 \(G_f\) 中的残存容量定义为:\[c_f(u,v) = \begin{cases} c(u,v) - f(u,v) & (u,v) \in E \\ f(v,u) & (v,u) \in E \end{cases}\]而根据流网络性质,\(c_f(u,v)\) 仅会取到上述的一个值。【画图说明】
由图可以看出,残存“网络”出现了反平行边,不符合流网络的定义,但除此以外都符合流网络的性质。
设 \(f'\) 为 \(G_f\) 中的一个流,定义 \(f \uparrow f'\) 为 \(f'\) 对 \(f\) 的递增,形式如:\[(f \uparrow f')(u, v) = f(u,v) + f'(u, v) - f'(v, u),(u,v) \in E \](即边 \((u,v)\) 的流量增加 \(f'(u,v)\),又减少了 \(f'(v,u)\))
根据数学推导,可以得到 \(f \uparrow f'\) 是 \(G\) 中的一个流(满足流量守恒和容量限制),且 \(|f \uparrow f| = |f| + |f'|\)
- 增广路径
给定流网络 \(G = (V, E)\) 和流 \(f\),增广路径 \(p\) 是残存网络 \(G_f\) 中一条从源点 \(s\) 到汇点 \(t\) 的简单路径。定义在一条增广路径 \(p\) 上能够为每条边增加的流量的最大值为 \(p\) 的残存容量(注意与残存网络中残存容量的区分),记作:\[c_f(p) = \min(c_f(u,v):(u,v) \in p) \]可以很直观地感受到,当 \(f\) 增加了大小为 \(c_f(p)\) 的流时,\(f\) 越接近最大流。
- 流网络的切割
流网络 \(G = (V,E)\) 中的一个切割 \((S,T)\) 将点集 \(V\) 划分为 \(S\) 和 \(T = V - S\) 两个集合,使得 \(s \in S\), \(t \in T\)。定义横跨切割 \((S,T)\) 的净流量 \(f(S,T)\) 如下:
wuhupai注:这其实就是在这个截面上流过去的流,这个流是流过去的减流回来的\[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) \]切割 \((S,T)\) 的容量是:\[c(S,T) = \sum_{u \in S}\sum_{v \in T}c(u,v) \]还是一样的,\((u,v) 和 (v,u)\) 只能有其中一条在 \(E\) 中,不存在的一条,其 \(|f| = 0\)。
一个网络的最小切割是整个网络中 \({容量最小}\) 的切割。
我们可以发现,切割容量只计算了从 \(S\) 到 \(T\) 的容量和,这与流的定义有所区别。
wuhupai注:这就是从S流向T的流量减去从T流向S的流量
wuhupai注:切割就是将一个图分成两半
wuhupai注:切割的容量就是在这个横截面上的边的容量之和
2. 引理证明【重要】:
-
对于给定的流 \(f\),横跨任何切割的净流量都相同,都等于 \(|f|\)。
wuhupai注:我喜欢感性理解,不妨将这张图看成一个并联电路,那么支路电流之和就等于干路电流。我任选一些支路,电流之和就等于干路电流
\(\triangle\)证明如下:
重写流量守恒式,\(\forall u \in V - \{s, t\}\)\[\sum_{v \in V} f(v,u) - \sum_{v \in V} f(u,v) = 0 \]根据 \(|f|\) 定义,加入上式(针对 \(S - \{s\}\) 部分的求和),
\[|f| = \sum_{v \in V}{f(s,v)} - \sum_{v \in V}{f(v,s)} + \sum_{v \in S - \{s\}} \left( \sum_{v \in V}{f(v,u)} - \sum_{v \in V}{f(u,v)} \right) \]展开重组得,
\[|f| = \sum_{v \in V} \left(f(s,v)+\sum_{v \in {S - \{s\}}}f(v,u) \right ) - \sum_{v \in V}\left(f(v,s) + \sum_{v \in S - \{s\}} f(u,v) \right) \]惊奇地发现,
\[|f| = \sum_{v \in V}\sum_{u \in S}f(u,v) - \sum_{v \in V}\sum_{u \in S}f(v,u) \](跟 \(f(S,T)\) 已经有点像了,集合不同)
因为 \(V = S \cup T\),且有 \(S \cap T = \varnothing\),可将针对 \(V\) 的求和,分解为针对 \(S\) 和 \(T\) 的求和,得到\[|f| = \sum_{u \in S} \left( \sum_{v \in S}f(u,v) + \sum_{v \in T}f(u,v) \right) - \sum_{u \in S} \left( \sum_{v \in S}f(v,u) + \sum_{v \in T}f(v,u) \right) \]"观察到",
\[|f| = \sum_{u \in S}\sum_{v \in T}f(u,v) - \sum_{u \in S}\sum_{v \in T}f(v,u) + \left( \sum_{u \in S}\sum_{v \in S}f(u,v) - \sum_{u \in S}\sum_{v \in S}f(v,u) \right ) \]括号内,\(u,v\) 对称,值为 \(0\)。
我们终于得到,\[|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) \]感性地理解,根据流量守恒和流网络的连通性( \(\forall u \in V, \exist s \rightarrow u \rightarrow t\) ),无论在哪里划分,从 \(S\) 到 \(T\) 的净流量总是恒定的,且等于 \(|f|\)。
-
流网络 \(G\) 中任意的 \(|f|\) 不能超过 \(G\) 任意的 \(c(S,T)\)。
wuhupai注:数学推导较好理解,也可以感性理解
\(\triangle\)证明如下:
设 \(f\) 为任意流,\((S,T)\) 为任意切割。\[|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) \]\[|f| \le \sum_{u \in S}\sum_{v \in T}f(u,v) \]\[\sum_{u \in S}\sum_{v \in T}f(u,v)-\sum_{u \in S}\sum_{v \in T}f(v,u) \le \sum_{u \in S}\sum_{v \in T}f(u,v) \]\[\sum_{u \in S}\sum_{v \in T}f(u,v) \le \sum_{u \in S}\sum_{v \in T}f(u,v) + \sum_{u \in S}\sum_{v \in T}f(v,u) \]\[\sum_{u \in S}\sum_{v \in T}f(u,v) \le \sum_{u \in S}\sum_{v \in T}c(u,v) \]\[|f| \le \sum_{u \in S}\sum_{v \in T}f(u,v) \le \sum_{u \in S}\sum_{v \in T}c(u,v) = c(S,T) \]最直观的结论是,最大流 \(\le\) 最小切割
-
最大流最小切割定理
设 \(f\) 为流网络 \(G = (V, E)\) 中的一个流,则有以下 \(3\) 个条件等价:
\(a\). \(f\) 是 \(G\) 的一个最大流
\(b\). 残存网络 \(G_f\) 不包括任何增广路径
\(c\). \(|f| = c(S,T)\),其中 \((S,T)\) 是 \(G\) 的某个切割
\(\triangle\)证明如下:
\(a \Rightarrow b\),显然
\(b \Rightarrow c\),假定 \(G_f\) 不包含任何增广路径 \(p\),即任意的 \(c_f(p) = 0\),在这里认为是"不存在" \(s\) 到 \(t\) 的路径。定义 \(S = \{v \in V : 在 G_f 中存在一条路径s \rightarrow v\}, T = V - S\)。显然有 \(s \in S, t \in T\),所以 \((S,T)\) 是 \(G\) 的一个切割。
设有 \(u \in S, v \in T\),- 若有 \((u,v) \in E\),则必有 \(f(u,v) = c(u,v)\),否则根据残存网络定义,\(c_f(u,v) = c(u,v) - f(u,v) > 0\),会有 \((u,v) \in E_f\),与条件 \(b\) 矛盾。
- 若有 \((v,u) \in E\),则必有 \(f(v,u) = 0\),否则根据残存网络定义,\(c_f(u,v) = f(v,u) > 0\),会有 \((u,v) \in E_f\),与条件 \(b\) 矛盾。
- 若有 \((u,v) \notin E,(v,u) \notin E\),则 \(f(u,v) = f(v,u) = c_f(u,v) = 0\),可行。
综上,可以得到,
\[|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) = \sum_{u \in S}\sum_{v \in T}c(u,v) - \sum_{u \in S}\sum_{v \in T}0 = c(S,T) \]此时,\(|f|\) 最大,\(c(S,T)\) 最小,即最大流 \(=\) 最小切割。
\(c \Rightarrow a\), 由证明 \(2\),\(|f| \le c(S,T)\),则此时 \(f\) 便为最大流。
wuhupai注:我们取出一个最大流和最小割,考虑这个最大流的残存网络,因为这是不存在增广路径,这时从S点出发能到达的集合和从T点出发能到达的点的集合就构成了一个割,所以流的集合和割的集合都有了这个元素,因为大于等于的关系,那么这个最大流就是最小割
3. Ford-Fulkerson思想:
- 步骤
\(|f| = 0\) \(\rightarrow\) 构建残存网络 \(G_f\) \(\rightarrow\) 寻找增广路径 \(p\) \(\rightarrow\) \(|f| = |f| + c_f(p)\) \(\rightarrow\) 直至 \(G_f\) 不存在 \(p\) 时结束。
- 随堂思考
为何最后找到的 \(|f|\) 是最大流,尝试用之前讲过的知识解释。
最大流最小切割定理的三个等价条件。
4. 多种实现方式:
本质都是 \(\texttt{Ford-Fulkerson}\) 思想,只不过实现效率不同。
wuhhupai注:new_hope几乎没讲反向边的作用,这里来讲一下
(画错了,懒)
这是一个我也不知道是什么的东西,但建图建出来就是这样,因该是流网络和残存网络的叠加体,边权为0的边是流网络,正的是残存网络(因为什么都没有操作)
比如说我找到了两条增广路径(只是演示)
那么我要找下一条增广路
(给节点标了个号)
此时我们发现原来1-2-3-6和1-4-3-6的增广路变成了1-4-3-6和1-2-5-6达到了找增广路的目的,所以这是对的,感性理解并且会用就行
————————————————————————————————————————————————
- \(\texttt{Edmonds−Karp}\) 算法
- 主要步骤:每次
bfs
搜一遍残存网络,找到一条增广路,更新最大流与残存网络。 - 时间复杂度:\(O(nm^2)\) (\(n\) 点数,\(m\) 边数)
- 主要步骤:每次
- \(\texttt{Dinic}\) 算法
- 主要步骤:每次
bfs
搜一遍残存网络,建分层图,找到多条可能的增广路,再dfs
找到增广路的残存容量,更新最大流与残存网络。 - 时间复杂度:\(O(n^2m)\) (\(n\) 点数,\(m\) 边数)
- 主要步骤:每次
// Edmonds−Karp 核心部分
int bfs() {
for(int i = 1; i <= n; i ++) vis[i] = 0;
queue<int> q;
q.push(s);
vis[s] = 1;
dis[s] = 2005020600;
while(!q.empty()) {
int x = q.front();
q.pop();
for(int i = head[x]; i; i = e[i].net) {
if(e[i].val == 0) continue;
int v = e[i].to;
if(vis[v] == 1) continue;
dis[v] = min(dis[x], e[i].val);
pre[v] = i;//记录路径
q.push(v);
vis[v] = 1;
if(v == t) return 1;
}
}
return 0;
} //寻找增广路
void update() {
int x = t;
while(x != s) {
int v = pre[x];
e[v].val -= dis[t];
e[v^1].val += dis[t];
x = e[v^1].to;
}
ans+=dis[t];
} //构建残存网络
//Dinic 核心部分
bool bfs() {
queue<int> q; memset(dep,0,sizeof(dep));
q.push(s), dep[s] = 1;
while(!q.empty()) {
int fr = q.front(); q.pop();
for(int i = head[fr]; i; i = edge[i].ne) {
int ed = edge[i].v, w = edge[i].c;
if(!dep[ed] && w) {
dep[ed] = dep[fr] + 1;
if(ed == t) return true;
q.push(ed);
}
}
}
return false;
} // 分层图找增广路
int dfs(int u,int minf) {
if(u == t) return minf;
int sum = 0;
for(int i = head[u]; i; i = edge[i].ne) {
int ed = edge[i].v, w = edge[i].c;
if(dep[ed] == dep[u] + 1 && w) {
int f = dfs(ed,min(minf,w));
edge[i].c -= f;
edge[i^1].c += f;
minf -= f;
sum += f;
if(minf == 0) break;
}
}
if(sum == 0) dep[u] = 0;
return sum;
} // 构建残存网络,更新最大流
视频模拟网络流
三、习题1-2
“网络流的题目难在建模。”——Rayleigh.
1. 最大权闭合子图问题
1. 前置知识
闭合子图:对于有向图 \(G\),存在点集 \(V\),满足点集内所有的点,他们的出边指向的点都是 \(V\) 内的点,将有向边添加上,就是一个子图。(可以有入边)
wuhupai注:
简单割:在一个割中,如果连接两个集合的边都是源点或者汇点的邻边,那么这个割被称为简单割。
最大权闭合子图即求解上述子图中点权和最大的子图。
如何将闭合子图变成流网络?
2. 步骤:
建一个源点,和一个汇点;
\(s\) 连向权值为正的点,流量赋为点权;
权值为负的点向 \(t\) 连一条边,流量赋为点权的绝对值;
点与点之间的有向边不变,流量赋为正无穷。
原理?
//这个v1假设成了闭合子图
割的所有情况共有四种( \(S\) 集合到 \(T\) 集合共四种情况),其中有两种情况不存在,第①种情况不存在是因为我们在建图时就不会建 \(s\) 到 \(t\) 的边,第②种情况不存在是根据闭合子图的定理,从 \(v_1\) 出发的点一定会回到 \(v_1\),不会到 \(v_2\)。
为啥割中没有 \(s \rightarrow v_1\) 的边,假设有,则会被分为 \(\{s\}, G_1, G_2\) 三个部分,与题设不符,再要是有 \(s \rightarrow v_1\) 而没有 \(s \rightarrow v_2\) 会怎么办?答案是此时 \(S\) 中同时存在了 \(s\) 和 \(t\),这与割的定义矛盾。
3. 数学推导:
设简单割 \((S,T)\),闭合子图 \(G_1 = (V_1, E_1)\),原图 \(G\) 的闭合子图的补图 \(G_2 = (V_2, E_2)\)
由图知,割中不存在 \(v_1 \rightarrow v_2\),和 \(s \rightarrow t\) 的边,只有 \(v_1 \rightarrow t\) 和 \(s \rightarrow v_2\) 的边,所以可以得到该割的容量 \(c(S,T)\) 为:
闭合子图的权值
两个量相加有(红色式子抵消),
最后得到,
在左项恒定的情况下,若要得到最大的 \(W\),则仅需求得最小的 \(c(S,T)\),而最大流最小切割定理告诉我们,这可以转化为最大流求得。
wuhupai注:这里我们求的是v1这个闭合子图的权值之和。
有啥用呢?
CF311E
为啥是网络流,怎么与最大权闭合子图建立联系?
分析题目,发现 \(0/1\) 修改比较麻烦,先假设所有的点都被改为 \(1\)。当前的答案是:所有 \(1\) 要求的 \(w\) 之和 \(-\) 所有修改为 \(0\) 点的 \(v\) 之和 \(-\) \(g\) 乘上 \(0\) 要求中要求倒给的个数。
现在,在上述的假设下,如果要满足一个 \(0\) 要求,那么该要求所有相关的点都必须进行修改,如果一个点进行了修改,那么该点所有有关的 \(1\) 要求都不满足。
这和之前 闭合子图中一个点被选择了则后继必须被选择 这一性质是一样的,我们考虑建图。
怎么建图?
注意接下来的分析还是基于所有点都被改为 \(1\) 的题设。
原先为 \(0\) 的点,因为之前已经被修改过一次,如果修改该点就相当于反悔,会产生 \(v\) 的贡献。
原先为 \(1\) 的点,如果修改该点,会产生 \(-v\) 的贡献。
对于每个 \(0\) 要求,如果将该要求改为满足,那么对答案会产生 \(w\) 贡献,如果该节点还要倒贴,一共会产生 \(w+g\) 贡献,将贡献作为点的权值,而且所有与它相关的点都必须选,于是连该要求到所有有关的点的边(出边)。
对于每个 \(1\) 要求,如果将该要求改为不满足,那么对答案会产生 \(-w\) 贡献,如果还要倒贴,一共会产生 \(-w-g\) 贡献,将贡献作为点的权值,而且所有与它相关的点一个被修改该要求也会不满足,所以连所有有关的点到该要求的边(入边)。
接着我们按照前面所讲的步骤,利用网络流求解最大权闭合子图问题。
补充:最终构造的流网络中,贡献为所有的所有 \(0\) 要求的 \(w\) 之和 \(+\) 所有 \(0\) 点的修改代价 \(+\) \(g\) 乘上 \(0\) 要求中要求倒给的个数 \(-\) 最小割,加上题设的答案,最终答案为 \(\sum w - MinCut\)
2. 图上问题
建模方式:每个点拆成入点和出点转成二分图匹配。
例:最小路径覆盖问题
给定一张图,求最少能用多少条边不相交的路径覆盖所有点。
考虑将每个点 \(u\) 拆成入点 \(in_u\) 和出点 \(out_u\),那么对于一条边 \((u,v)\),它的作用就是将 \(in_u\) 所属的路径与 \(out_u\) 所属的路径合并,使得路径数 \(-1\)。
由于路径不能相交,所以每个 \(in_u\) 和 \(out_u\) 只能合并一次,那么就相当于初始时有 \(n\) 条路径,我们希望用若干条边减去最多的路径数。
该数目就等于以 \(in_u\),\(out_u\) 为点,以 \((in_u,out_u)\) 为边的二分图最大匹配。
于是有:最小路径覆盖数 = 总点数 - 最大匹配数。
LuoGu P2764
思考:用匈牙利算法和最大流算法分别如何实现路径输出
匈牙利算法:利用数组 \(match[]\) 递归输出。
bool find(int x) {
for(int it : g[x]) {
if(vis[it]) continue;
vis[it] = 1;
if(!match[it] || find(match[it]))
match[it] = x; return true;
}
return false;
}
void rec(int u) {
r[t].push_back(u);
for(int it : g[u])
if(match[it] == u) rec(it);
return;
}
void solve() {
int ct = 0;
for(int i = 1; i <= n; i ++) {
memset(vis, 0, sizeof(vis));
ct += find(i);
}
for(int i = 1; i <= n; i ++)
if(!match[i]) t ++, rec(i);
for(int i = 1; i <= t; i ++) {
for(int it : r[i]) cout << it << " ";
cout << "\n";
}
cout << n - ct;
}
最大流算法:在残存网络中寻找满流的边。
留给大家发挥
图上问题其实都与之前二分图的解法相同,不再过多介绍。
3. 最小割问题
广义地来说,最大权闭合子图问题可归类于最小割问题,但发现答案具有闭合子图的性质较难(对于讲者来说),最小割通常为“选择”问题。
一类“选择”问题:选择若干个物品使得收益之和最大(即没选的物品收益之和最小),满足约束。
建模方式:
源点向每个物品连边,边权为收益,割掉该边表示不选该物品;
每个物品向汇点连边,边权为代价,割掉该边表示选该物品;
中间的连边视题目约束而定。
最大值:\(Ans = \sum val - MinCut\)
这里不太好理解的地方在于,\(-MinCut\) 实际上是对于答案的贡献,比如一个物品 \((val_i,w_i)\),其价值 \(val\) 已被统计到 \(\sum val\) 中,所以有选该物品则需带上代价 \(w_i\),不选该物品要撤销价值 \(v_i\)。
Luogu P1646
如果我们暂且不考虑第 \(3 \~\ 6\) 个矩阵,怎么做?
非常简单,对于一个人拆成两个点,\(s\) 连向 \(u_1\) 代表选理科,\(u_2\) 连向 \(t\) 代表选文科,\(u_1\) 连向 \(u_2\) ,容量正无穷,根据上述套路求解即可。
考虑如何体现 \((i,j)\) 同时/不同时选文科的贡献?
首先,如果同时选文科肯定会有新增的贡献,根据得到答案的套路,我们肯定是要新增一个节点并与 \(s\) 相连。
\((i,j)\) 不同时选文科时,假设有 \(s \rightarrow i\) 而没有 \(s \rightarrow j\),同时也有 \(j \rightarrow t\) 而没有 \(i \rightarrow t\)。
\((i,j)\) 同时选文科时,有 \(s \rightarrow i\) 和 \(s \rightarrow j\),同时没有 \(j \rightarrow t\) 和 \(i \rightarrow t\)。
充分发扬人类智慧,新增一个节点 \(p\),从 \(s\) 连一条容量为都选择文科的贡献到 \(p\),从 \(p\) 连向 \(i,j\) 的边容量为正无穷。【画图说明】(理科操作同理)
4. 总结
建模还是得多练习才会有经验的,具体的其他 trick 包括但不限于:拆点限流,加入时间变量等,这些要等各位自己去发现总结了。
四、费用流
最小(大)费用最大流:在满足最大流的前提下使得所花费用最少(多)。
实现:寻找增广路时使用SPFA
处理。
方式:基于 \(\texttt{EK}\) 或 \(\texttt{Dinic}\)。
Luogu P3381 【模板】最小费用最大流
板题,这里是基于 \(\texttt{EK}\) 的代码。(似乎费用流中 \(\texttt{EK}\) 用得多些)
bool spfa()
{
clear();
queue<int> q;
q.push(s); vis[s] = 1; mxcf[s] = inf; dis[s] = 0;
while(q.size())
{
int fr = q.front();
q.pop(); vis[fr] = 0;
for(int i = hd[fr]; i; i = e[i].nx)
{
int v = e[i].v, C = e[i].C, F = e[i].F;
if(C && dis[v] > dis[fr] + F)
{
dis[v] = dis[fr] + F;
if(!vis[v]) { q.push(v); vis[v] = 1; }
mxcf[v] = min(mxcf[fr], C);
pre[v] = i;
}
}
}
return mxcf[t] > 0;
}
void update()
{
int v = t;
do {
int id = pre[v];
e[id].C -= mxcf[t], e[id ^ 1].C += mxcf[t];
v = e[id ^ 1].v;
} while(v != s);
mxFl += mxcf[t];
mnFe += mxcf[t] * dis[t];
}
LuoGu P6577 【模板】二分图最大权完美匹配
理解透后应该也可以写了。
CF277E Binary Tree on Plane
约束——二叉树的性质:“树”和“二叉”的性质。
想清楚上面这个问题大概就好做了。
树的性质:一个节点必须是一个节点的儿子。
二叉树的性质:一个节点最多有两个儿子。
建图?判断无解?
可能会有同学想到拆点限流,但这道题对于一个点有双重限制,所以这里使用拆点。
我们根据这两个约束对一个节点拆为 \(u_{fa}\) 和 \(u_{son}\) 两个点,\(s\) 向 \(u_{fa}\) 连容量为 \(2\) 的边,代价 \(0\),代表可最多容纳两个儿子,\(u_{son}\) 向 \(t\) 连容量为 \(1\) 的边,代价 \(0\),代表最多成为一个节点的儿子。\(u_{fa}\) 向 \(v_{son}\) 连容量 \(1\),代价距离的边。最终一条 \(s \rightarrow u_{fa} \rightarrow v_{son} \rightarrow t\) 的路径代表 \(u\) 成为了 \(v\) 的父亲。
\(\triangle\) 另一方面,最大流中很少出现带小数的流,所以考虑将距离作为代价。
LuoGu P4066
路径不相交问题经典思路:若有两条路径相交,则可交换两人身份,具体在这道题也有体现,\(\text{Dong Shu Cheng}\) 的一篇题解中也有涉及。(所以本质上“不相交”只是用来迷惑的)
建图?
应该能想到,对于每个点的限制拆成 (入点,出点,边) 考虑,连边细节留给大家,最后跑最大费用最大流即可。
Tips:连边需要优化。(最大流复杂度)
网络流和最大流的建模技巧非常相似,只是加上了一些限制,大家可以在练习中慢慢体会。
五、结语
希望大家听完有所收获
六、文献参考:
模型:
https://www.cnblogs.com/YSFAC/p/13206167.html
https://zhuanlan.zhihu.com/p/553454908
https://blog.csdn.net/GROZAX/article/details/128512825
https://blog.csdn.net/qq_45735851/article/details/113811882
例题:
洛谷
OI - wiki
基础理论:
算法导论