网络流初步详解

网络流

定义

网络流图定义

  1. 每条边有两个权值,容量和流量
  2. 流量不超过每条边的容量
  3. 流量守恒,每个点流进来和流出去的容量一样
  4. 源点和汇点容量无限

定义\(E\)为边集,\(V\)为点集

最大流

\(\sum _{v \in V} f(s,v)\) 最大

真实定义:

\(|f| = \sum _{v \in V} f(s,v) - \sum_{v \in V} f(v,s)\)

由于一般构造的网络流图没有流向起点的边,所以一般不用管(但是分析的时候要考虑

问题:

  1. 有反向边的图如何转化使得最大流不变?

    如果有二元环,新建节点连边(拆点

    怎么拆?听甲鱼讲课啊QwQ

  2. 多源汇最大流

    连一个超级源点和一个超级汇点,原来的源点汇点连的边容量为\(+inf\)

    举个例子:二分图最大匹配

最大流建模

  1. 是否存在s到t可经过相同节点不经过相同边的两条路径

    边容量设置为1

  2. 是否存在不经过中间相同节点的两条路径

    点转化为边(拆成两个点中间连条边),容量设置为1

  3. 满足以上两种情况

    同时设置即可

可见最大流模型的一般建模思路是运用流的容量限制,使得题目中的约束得以满足,有时还需使用一些特殊的方法(如上的拆点)来满足题目的特别约束。

残量网络

容量为原容量减去流量

还包含所有原图中的反向边,c(v,u)=-c(u,v)

走反向边的时候,相当于把原来的流量减为0

增广

定义残量网络中的一个流 f'

\(f ↑ f' (u,v)=f(u,v)+f'(u,v)-f'(v,u)\)

什么意思呢?

如果残量网络上有这么一条增广路,使得从\(s\)\(t\)的流量最小值\(\ge 1\)(至少还有1的流量可以通过)

那么把这些流量加到最大流,更新最大流

引理

\(f ↑ f' = |f|+|f'|\)

相当于把剩下可流的流量直接流掉

这个剩下的可能是撤销原来一条弧,增加另一个地方的新弧(反向边的作用

证明

说下思路

  1. 流量不超过容量限制

    显然,参考残量网络定义

  2. 流量守恒

    把全部拆开,显然各自流量守恒

    然后改变方向合在一起就证出来了

具体?听甲鱼讲课呀QwQ

求流量

大概是把正向子图和反向子图增广拆开

然后合并4项和两项,得到在并图中增广就可以了

增广路

残量网络中从源到汇中的一条简单路径,一个边集

流量相当于残量网络中的瓶颈,即每条边容量的最小值

结论

  1. 增广之后流量增加

    运用上面的引理,

    \(|f↑f_p| = |f| +|f_p|\)

    然后对于任意增广路,\(|f_p|>1\)

    不然就不事增广路力(不是一条路径

  2. 当找不到增广路时当前流是最大流

    这个证明有点复杂

    于是我们通过此问题引入新概念

即把\(V\)划分成\(S\)\(T\)两个子图,定义应该是一个边集,就是\(\sum _{u\in S}\sum_{v\in T} (u,v) \and (v,u) \in E\)(超级不严谨

割的流量

\(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)\)

割的容量

$c(S,T)=\sum _{u \in S}\sum _{v \in T}c(u,v) $

最小割

所有割中容量最小的那个

引理2

对于流\(f\),任意割之间的流量不变

感性理解:流量守恒

还是证明一下吧

\[|f|=\sum _{v\in V} f(s,v) - \sum_{v\in V}f(v,s) \]

\[= \sum _{v\in V} f(s,v) - \sum_{v\in V}f(v,s)+\sum _{u \in S-s} \sum _{v\in V}f(u,v) -\sum _{u \in S-s} \sum _{v\in V}f(u,v)- \sum _{v\in V}\sum _{u \in S-s}f(v,u) + \sum _{v\in V}\sum _{u \in S-s}f(v,u) \]

\[=\sum _{u\in S} \sum _{v\in V} f(u,v)-\sum _{u \in S-s}\sum _{v\in V} f(u,v)- \sum _{v\in V}\sum _{u\in S} f(v,u)+\sum _{v\in V}\sum _{u \in S-s} f(v,u) \]

\[=\sum _{u\in S} \sum _{v\in T} f(u,v)+\sum _{u\in S} \sum _{v\in S} f(u,v) - \sum _{u \in S-s}\sum _{v\in V} f(u,v)- \sum _{v\in T}\sum _{u\in S} f(v,u)-\sum _{v\in S}\sum _{u\in S} f(v,u)+\sum _{v\in V}\sum _{u \in S-s} f(v,u) \]

然后2,3,5,6四项相消(换一下sigma顺序就一模一样了)

得到:

\(=\sum _{u \in S}\sum _{v \in T} f(u,v) - \sum _{u \in S} \sum _{v \in T} f(v,u)\)

\(=f(u,v)\)

推论

任意\(|f|\)的流量不超过任意割的容量

\(|f| =f(S,T)\leq c(S,T)\)

很显然

最小割最大流定理

这个定理包含三个命题:

  • \(f\)是图\(G\)的最大流——①

  • 当前流 f 的残量网络\(c_f\)上不存在增广路——②

  • 存在某个割使得\(|f| =c(S, T)\)成立。由结论\(2\)可知,满足条件的割\(c\)必定是最小割——③

    这三个命题等价

证明

  1. 证明①推②:

    令④为存在\(|f_2|>|f|\)

    因为\(f\)是当前的最大流,所以①成立时④不成立

    参考增广路的定义,存在增广路与可以构造\(|f_2|>|f|\)\(f\)为当前流量),即②的否命题与④成逆否命题

    当①成立时,④不成立,②的否命题不成立,②成立

  2. 构造点集\(S\)\(s\)在残量网络上能够到达的点集,\(T=V-S\),定义那么\(t\)一定在\(T\)中(\(T\)中至少有一个\(t\)),\((S,T)\)是一个割

    即当②成立时③成立

  3. 先康康上面的推论

    高中数学告诉我们,两边取任意,左边的最大值就等于右边的最小值

    于是有最大流等于最小割

    即最小割是最大流的充分条件,当③成立时①成立

由上面三个结论可以得到它们是互相的充要条件

网络流算法

Ford-Fulkerson

每次用BFS找任意一条增广路然后增加流量直到残量网络中s与t不连通

很naiive

Edmonds-Karp

每次寻找最短的增广路(BFS)

引理

增广前后s到每个点的距离不降

选取\(v\)使得\(df'(v)\) 是d下降的节点中最小的

则有\(df'(v) <df(v)\)

设u为v的前驱节点,则\(df'(u) <df'(v)\)

然后证明\((u,v)\)不在增广前的网络\(Ef\)

如果在,\(df(v)\leq df(u)+1\leq df'(u)+1=df'(v)\)

矛盾,所以不在

因为\(df(v)\)在本次变化,所以\((u,v) \in Ef'\),

即有 \(df(v)=df(u)-1<=df'(u)-1=df'(v)-2\)

所以\(v\)不存在,其逆命题成立

EK增广\(O(nm)\)

\(nm^2\)

Dinic​

\(dinic\) 的原理是先把原图分层,然后一次DFS找完当前残量网络中所有增广路,每次增广一个阻塞流

于是\(d\)一定单增,所以最多增广\(V\)

又因为单次最大是\(O(VE)\)的(多次经过的点会最多被E条边更新(每条边最多被增广一次),最大V个点都这样被更新)

于是总复杂度是\(O(V^2E)\)也就是\(O(n^2m)\)

搞个板子

#include<iostream>
#include<cstdio> 
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
#define CLR(x) memset(x,0,sizeof(x))
#define setmi(x) memset(x,-1,sizeof(x))
using namespace std;
const int N = 200021;
struct node{
	int v,w,nex;
}edge[N<<1];
int top=1,head[N],cur[N];
const int inf=192608170;
inline void add(int u,int v,int w){
	edge[++top].v=v;
	edge[top].w=w;
	edge[top].nex=head[u];
	head[u]=top;
}
inline int read(){
	char ch=getchar();int x=0;int pos=1;
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x; 
}
int n,m,s,t,ui,vi,wi;
class dinic{
	private:
		int level[N];
		queue<int>q;
	public:
		inline int bfs(){
			setmi(level);level[s]=0;
			q.push(s);
			while(!q.empty()){
				int now=q.front();q.pop();
				for(int i=head[now];i;i=edge[i].nex){
					int v=edge[i].v;
					if(level[v]==-1&&edge[i].w){
						level[v]=level[now]+1;
						q.push(v);
					}
				}
			}
			if(level[t]==-1) return 0;
			else return 1;
		}
		inline int dfs(int now,int flow){
			if(now==t||flow==0) return flow;
			int res=flow;
			for(register int &i=cur[now];i;i=edge[i].nex){
				if(res<=0) break;
				int v=edge[i].v;
				if(edge[i].w&&level[v]==level[now]+1){
					int away=dfs(v,min(edge[i].w,res));
					res-=away,edge[i].w-=away,edge[i^1].w+=away;
				}
			}
			cur[now]=head[now];
			return flow-res;
		}
		inline int solve(){
			int ans=0;
			while(bfs()){
				ans+=dfs(s,inf);
			}
			return ans;
		}
}DI;
int main(){
	n=read(),m=read(),s=read(),t=read();
	rep(i,1,m){
		ui=read(),vi=read(),wi=read();
		add(ui,vi,wi);
		add(vi,ui,0);
	}
	rep(i,1,n){
		cur[i]=head[i];
	}
	printf("%d",DI.solve());
	return 0;
}

中等常数,最大点64ms,不想卡

LCT优化

不会,待填坑

加上之后炒鸡块!(也就\(nm\log n\))

当前弧优化

显然每条边只会增广一次,更新head数组即可

具体做法是复制一个head的cur(head分层时还有用)

然后这样写:for(int &i = cur[now] ; i ; i = edge[i].nex)

单位容量网络

  1. 增广次数不超过\(E^{\frac{1}{2}}\)

    1. \(d(t)\le E^{\frac{1}{2}}\)

      增广至少\(E^{\frac{1}{2}}\)

    2. \(d(t) \geq E^{\frac{1}{2}}\)

      在递归到第二步时,至少已经走了\(E^{\frac{1}{2}}\)

      所以至少有两层之间边数不超过\(E^{\frac{1}{2}}\),又因为每条边容量为1,即边数为两层之间的割的容量\(>\)割的流量\(=\)残量网络最大流

      所以\(d\)的递增不超过\(E^\frac{1}{2}\)次,即增广次数

  2. 增广次数不超过\(V^{\frac{2}{3}}\)

    1. \(d(t)\leq V^{\frac{2}{3}}\)

    2. \(d(t)\geq V^{\frac{2}{3}}\)

      类似地,一定存在相邻的两层满足点数\(\leq V^{\frac{1}{3}}\)

      于是边数一定\(\leq (V^{\frac{1}{3}})^2=V^{\frac{2}{3}}\)

      同上,容量\(>\)最小割等于最大流,于是增广不超过\(V^{\frac{2}{3}}\)

于是增广次数小于\(min(V^{\frac{2}{3}},E^{\frac{1}{2}})\)

然后经过每个点和每条边经过的增广路数量不超过 \(E\)

于是总复杂度\(O(E*min(V^{\frac{2}{3}},E^{\frac{1}{2}}))\)

单位网络

  1. \(d(t)\leq V^{\frac{1}{2}}\)

  2. \(d(t) \geq V^{\frac{1}{2}}\)

    类似,可以找到大小\(\leq V^{\frac{1}{2}}\)的割

可以证明跑二分图匹配复杂度

例题

最大权闭合子图

选子图相当于把一个图割成两部分,代表选或者不选

建边方式:所有的正权值连S,负权值连T,原图边连inf(防止被割

最小割产生的图S和图T,图S为最大权闭合子图

证明:

 割集中所有的边,不是连接在s上,就是连接在t上

 我们记割集中,所有连接在s上的边的权值和为\(x_1\),所有连接在t上的边的权值和为\(x_2\),而割集中所有边权值和为\(X=x_1+x_2\)

 令图S中所有点的权值和为\(W\),记其中正权值之和为\(w_1\),负权值之和为 \(-w_2\),故\(W=w_1-w_2\)

 而\(W+X=w_1-w_2+x_1+x_2\),又因为\(x_2=w_2\)\(W+X = w_1 + x_1\)

 而显然的,\(w1+x1\)是整个图中所有正权值之和,记为\(SUM\)

 然后有\(W = SUM - X\),“图S中所有点的权值和” = “整个图中所有正权值之和” - “割集中所有边权值和”

 然后,因为SUM为定值,只要我们取最小割,则“图S中所有点的权值和”就是最大的,即此时图S为图S为最大权闭合子图

这谁想得到啊,,,

海拔

左上点海拔0,右下1,显然是左上一堆点都是0,右下一堆是1,然后取0和1交界处最小值就可以了,暴力DINIC会T

可以转对偶图然后跑最短路

后记

什么?你问我为什么没有后面的题了?

当然是因为我只是一个刚拿到普及奖的蒟蒻啊QwQ

集训以来首次自闭祭

先去搞字符串了

posted @ 2019-08-01 21:05  lcyfrog  阅读(883)  评论(0编辑  收藏  举报