网络流学习笔记

图论的证明太复杂了,就只说一下算法流程理解一下好了。


一. 相关定义

  1. 容量网络:设 G ( V , E ) G(V,E) G(V,E),是一个有向网络,在 V V V中指定了一个顶点,称为源点(记为 V s V_s Vs),以及另一个顶点,称为汇点(记为 V t V_t Vt);对于每一条弧 ( u , v ) (u,v) (u,v)属于 E E E,对应有一个权值 c ( u , v ) > 0 c(u,v)>0 c(u,v)>0,称为弧的容量。通常把这样的有向网络 G G G称为容量网络。
  2. 弧的流量:通过容量网络 G G G中每条弧 ( u , v ) (u,v) (u,v)上的实际流量(简称流量),记为 f ( u , v ) f(u,v) f(u,v)
  3. 反向弧:当一个弧流过 f ( u , v ) f(u,v) f(u,v)的流量时,我们建立一条 ( v , u ) (v,u) (v,u)的反向弧,并把反向弧的容量增加 f ( u , v ) f(u,v) f(u,v),当流量在反向弧上流过时,再把此流量增加到 ( u , v ) (u,v) (u,v)的容量当中。
  4. 网络流:所有弧上流量的集合 f = { f ( u , v ) } f=\{f(u,v)\} f={f(u,v)},称为该容量网络的一个网络流。
  5. 可行流:在容量网络 G G G中满足以下条件的网络流 f f f
    a. 容量限制: 0 ≤ f ( u , v ) ≤ c ( u , v ) 0\le f(u,v)\le c(u,v) 0f(u,v)c(u,v)
    b. 流量平衡:除源点和汇点外,流入一个点的流量要等于流出这个点的流量;
    c. 斜对称性: f ( u , v ) = − f ( v , u ) f(u,v)=-f(v,u) f(u,v)=f(v,u)
  6. 最大流:在容量网络中,具有最大流量的可行流,称为网络最大流,简称最大流。
  7. 残量网络:网络中的每条边(包括反向弧)上容量与流量之差构成残量网络。
  8. 增广:残量网络中任何一条从 s s s t t t的有向道路中所有残量的最小值 d d d,把对应的所有边上的流量增加 d d d,这个过程叫做增广。
  9. 增广路定理:网络达到最大流当且仅当残留网络中没有增广路。

二. 最大流

1.反向边

反向边的意义在于,可以处理再找增广路的时候撤回之前路的流量。

设一条边为 ( u , v ) (u,v) (u,v),则其反向边为 ( v , u ) (v,u) (v,u)
实质上,每走一次 ( v , u ) (v,u) (v,u)相当于本次增广的路径中,从 v v v到汇点的路径走原来流过 ( u , v ) (u,v) (u,v)之后到从 v v v到汇点的路径,然后我们再去给原来那条走 ( u , v ) (u,v) (u,v)的路径从 u u u点找一条其他的到汇点的路径,实现增广。
图一图二
左图是一个残量网络,右图是一个走了反向边的增广路,具体来说,原来路径为 1 → 2 → 3 → 4 1\to 2 \to 3 \to 4 1234,现在在 3 3 3走反向边的实质为:本次的增广路(红色) 1 → 3 1\to 3 13后面的路走原来 1 → 2 → 3 → 4 1\to 2 \to 3 \to 4 1234路径中的 3 → 4 3\to 4 34,然后我们从 2 2 2开始,再找一条到 4 4 4的路,来代替 1 → 2 → 3 → 4 1\to 2 \to 3 \to 4 1234

2.增广路算法

只要我们不停的找从源点到汇点的增广路,根据增广路定理,当我们找完了所有的增广路,就可以得到最大流了。

  • 首先我们可以从源点开始,不断的进行dfs,每次找一个残量网络中能到达汇点的路线,给路线上所有的边减去路线上的最小的残余流量,这是 FF \text{FF} FF算法,时间复杂度为 O ( V E ) O(VE) O(VE),其中 V V V是最大流, E E E是边数。
  • 我们如果通过 BFS \text{BFS} BFS找到一条从源点到汇点的最短的增广路,并将其增广,可以证明最多需要增广 n × m n\times m n×m,次这就是 EK \text{EK} EK算法,时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2)
  • 很显然, BFS \text{BFS} BFS一次来找一条最短的增广路比较浪费,所以我们考虑进行一次 BFS \text{BFS} BFS后,将图进行分层,这样如果我们按照分的层来找增广路,找到的增广路一定是最短的;在分层的基础上,我们用 DFS \text{DFS} DFS进行增广,直到找不到增广路为止;然后我们继续给新的残量网络分层,继续操作。这个算法名叫 Dinic \text{Dinic} Dinic。不难发现,每进行一次分层,从源点到汇点的层数至少加 1 1 1,所以我们最多分 n − 1 n-1 n1次层,每次找增广路的复杂度为 O ( n m ) O(nm) O(nm),所以 Dinic \text{Dinic} Dinic的时间复杂度上限为 O ( n 2 m ) O(n^2m) O(n2m)。可以证明,对于二分图 Dinic \text{Dinic} Dinic的时间复杂度为 O ( n m ) O(\sqrt{n}m) O(n m)
  • 关于Dinic的当前弧优化(效果显著的常数优化)
    在一次dfs的过程中,如果一个点的一条边的流量用完了,那下一次在访问到这个点的时候就没有必要再次访问这条边了,我们只需要记录现在访问到的弧的位置,并在当前结单流量用完后及时跳出循环,下次用记录的位置开始,就是当前弧优化。

三.最小割

最小割定义为找一组边权最小的边集,并去掉,让 S S S T T T不连通。
有最大流最小割定理可以证明,最大流等于最小割。

四.最小费用最大流

在保证最大流量的基础上,有很多种方案满足要求,如果每条边还有一个边权,若总费用为所有边的边权与流量的乘积之和,最小费用最大流就是总费用最小的方案。
关于算法,直接把 EK \text{EK} EK算法的 BFS \text{BFS} BFS换成 SPFA \text{SPFA} SPFA,每次走最短路进行增广。

#include <cstdio>
#include <algorithm>

const int inf = 1e9;
const int maxn = 200010;

int cnt = 1;
int head[maxn], nxt[maxn], to[maxn], fl[maxn], v[maxn];
int q[maxn], dis[maxn], S, T, n, m, MX;
int pre[maxn], epre[maxn];
bool vis[maxn];

void add(int a, int b, int c, int d){
	nxt[++ cnt] = head[a], head[a] = cnt, to[cnt] = b, fl[cnt] = c, v[cnt] = d;
	nxt[++ cnt] = head[b], head[b] = cnt, to[cnt] = a, fl[cnt] = 0, v[cnt] = -d;
}

bool spfa(){
	for(int i = 1; i <= MX; i ++) dis[i] = inf, vis[i] = 0;
	vis[S] = 1, dis[S] = 0;
	int l = 1, r = 0;
	q[++ r] = S;
	while(l <= r){
		int x = q[l ++];
		for(int i = head[x]; i; i = nxt[i]){
			int u = to[i];
			if(fl[i] > 0 && dis[u] > dis[x] + v[i]){
				dis[u] = dis[x] + v[i];
				pre[u] = x, epre[u] = i;
				if(vis[to[i]] == 0) vis[to[i]] = 1, q[++ r] = to[i];
			}
		}
		vis[x] = 0;
	}
	if(dis[T] != inf) return true;
	return false;
}

void work(){
	int flow = 0, cost = 0;
	while(spfa()){
		int f = inf;
		for(int x = T; x != S; x = pre[x]){
			if(fl[epre[x]] < f) f = fl[epre[x]];
		}
		cost += f*dis[T], flow += f;
		for(int x = T; x != S; x = pre[x]){
			fl[epre[x]] -= f, fl[epre[x]^1] += f;
		}
	}
	printf("%d %d\n", flow, cost);
}

int main(){
	scanf("%d%d%d%d", &n, &m, &S, &T);
	MX = n;
	for(int i = 1; i <= m; i ++){
		int a, b, c, d;
		scanf("%d%d%d%d", &a, &b, &c, &d);
		add(a, b, c, d);
	}
	work();
	return 0;
}

五.网络流建模

1.最大流
(1)每个人选两个不同种类的物品

描述:一群人都要分别从A类物品中选一个,B类物品中选一个,求最多可以满足多少人。
做法:为了保证一个人只从一类物品中选择一个,要分别把两类物品与 S S S T T T相连,容量为物品数量。为了保证一个人只能分别取一样,要把一个人拆成两个点,两点之间容量为 1 1 1。人放在中间,两类物品放在两边。
例1:Dining

(2)二分图最大匹配

描述:求一个二分图的最大匹配。
做法:源点和汇点分别连接一侧的点,容量为 1 1 1,原来的二分图每条边,容量为 1 1 1
例1:A Plug for UNIX

2.最小割

最小割建模考虑把现在已有的点分为两部分,构造连边使分割方法对应相应的代价。

3.费用流
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值