最大流 最小割

  • 网络流的一些定义和性质

1.流网络(Flow Network)

流网络为一个有向图G=(V,E),所有边(u,v)E 都有一个容量(Capacity),用c(u,v)表示。另外规定了两个点:源点(Source)、汇点(Sink),分别为st(st)

2.流

  • 定义流量为f(u,v)V×VR的函数,表示经过(u,v)这条边的流量。

  • cf(u,v) = c(u,v)f(u,v)称为边(u,v)的剩余容量。

  • 整个网络的流量为|f| = (s,v)Ef(s,v) = (u,t)Ef(u,t)

3.流量函数满足的性质:

  • 容量限制: 流经某一条边的流量不得超过其容量,即:f(u,v)c(u,v)

  • 流量守恒: 对于除了源点汇点的任意节点 x,满足: uVf(u,x) = vVf(x,v)

  • 斜对称性:每条边流量与其反向边流量之和为0,即:f(u,v)+f(v,u)=0

  • 最大流

1.描述

对于给定的流网络,要求求出一个流f,使得|f| = (s,v)Ef(s,v) = (u,t)Ef(u,t)最大。

2.几个定义

  • 残量网络(Residual Network):G中所有剩余容量大于0的边构成的子图称为残量网络,用Gf表示。

  • 增广路(Augmenting Path): Gf上一条从源点s到汇点t的路径称为增广路。

3.Edmonds–Karp 算法

Ford–Fulkerson 增广: 是一种通过寻找增广路求解最大流的方法,步骤如下:

  • 1.寻找一条增广路,边集为Ea

  • 2.令fm = max{cf(u,v)}( (u,v) Ea ),对于每一条边(u,v)Ea,将 f(u,v) += fm,同时根据斜对称性,将 f(v,u) = fm

  • 3.再次建立残量网络,重复1步骤,直至找不到增广路。

Edmonds–Karp 算法即按照上面Ford–Fulkerson 增广的步骤,使用BFS寻找增广路。算法的时间复杂度为O(|V||E|2)

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 20010;
const int M = 200010;
int nextt[M], head[N], to[M], cnt = 1;
int n, m, tot = 0, s, t;
lld w[M];
bool vis[N];
void add(int x, int y, lld z) {
	nextt[++cnt] = head[x]; to[cnt] = y;
	w[cnt] = z; head[x] = cnt;
}
int edge[N], nxt[N];
bool bfs() {
	queue <int> q;
	memset(vis, false, sizeof(vis));
	q.push(s); vis[s] = true; tot = 0;
	memset(edge, 0 ,sizeof(edge)); memset(nxt, 0, sizeof(nxt));
	while(!q.empty()) {
		int u = q.front(); q.pop();
		if(u == t) return true;
		for(int i = head[u]; i; i = nextt[i]) {
			int v = to[i];
			if(w[i] > 0 && !vis[v]) {
				q.push(v);
				edge[v] = i; nxt[v] = u;
				if(!vis[v]) vis[v] = true;
			}
		}
	}
	return false;
}
lld EK() {
	lld ans = 0;
	while(bfs()) {
		memset(vis, 0, sizeof(vis)); tot = 0;
		lld wi = 1e10;
		for(int i = t; i != s; i = nxt[i]) wi = min(wi, w[edge[i]]);
		for(int i = t; i != s; i = nxt[i]) 
			w[edge[i]] -= wi, w[edge[i] ^ 1] += wi;
		  ans += wi;
	}
	return ans;
}
int main() {
//	freopen("data.in", "r", stdin);
	scanf("%d%d%d%d", &n, &m, &s, &t);
	for(int i = 1, u, v; i <= m; i++) {
    lld w; cin >> u >> v >> w;
    add(u, v, w); add(v, u, 0);
	}
	cout << EK() << endl;
	return 0;
}

4.Dinic 算法

两个定义:

  • 层次图(Level Graph):Gf进行BFS分层,从源点出发经过的边数相同的点为同一层,记d(u)为节点u的层数。分层之后保留到达下一层的边,删除同层之间的边从而得到层次图GL,即:GL=(V,EL)EL={(u,v) | (u,v)Ef,d(v)=d(u)+1}

  • 阻塞流(Blocking Flow):GL找到一个流fb,使得GL无法继续增广,则称fbGL的阻塞流。

Dinic 算法过程:

  • 1.在Gf上进行BFS求出层次图GL

  • 2.在GL上求出一个阻塞流fb

  • 3.将阻塞流fb加入到流 f中,更新残量网络Gf

  • 4.重复执行1,2,3过程,直到无法找到阻塞流(层次图中st不连通)

复杂度O(|V|2|E|)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 10005;
const int M = 200005;
int nextt[M], head[N], to[M];
lld w[M];
int n, m, s, t, cnt = 1;
int dis[N], cur[N];
void add(int x, int y, lld z) {
	nextt[++cnt] = head[x]; w[cnt] = z;
	to[cnt] = y; head[x] = cnt;
}
bool bfs() {
	queue <int> q;
  memset(dis, 0, sizeof(dis));
	q.push(s); dis[s] = 1;
  bool flag = false;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = head[u]; i; i = nextt[i]) {
			int v = to[i]; 
			if(w[i] > 0 && !dis[v]) {
				dis[v] = dis[u] + 1; q.push(v);
        if(v == t) flag = true;
			}
		}
	}
  return flag;
}
lld dfs(int u, lld mine) {
	lld minedge = 0;
	if(u == t) return mine;
	for(int i = cur[u]; i; i = nextt[i]) {
		int v = to[i]; cur[u] = i;
		if(w[i] > 0 && dis[v] == dis[u] + 1) {
		  lld now = dfs(v, min(mine, w[i]));
      minedge += now;
			w[i] -= now; w[i ^ 1] += now;
			mine -= now;
			if(!mine) break;
		}
	}
	return minedge;
}
void dinic() {
	lld ans = 0;
	while(bfs()) {
    memcpy(cur, head, sizeof(head));
		ans += dfs(s, 1e10);
	}
	cout << ans << endl;
}

int main() {
	// freopen("data.in", "r", stdin);
	cin >> n >> m >> s >> t;
	for(int i = 1, u, v; i <= m; i++) {
    lld z; cin >> u >> v >> z;
		add(u, v, z); add(v, u, 0);
	}
	dinic();
	return 0;
}

当前弧优化: DFS时,每次增广完一条路径(u,v)之后,(u,v)的容量所剩无几,下一次到达u时直接选择跳过(u,v),从下一条边开始增广。在代码中,使用cur[u]数组记录点u第一条未使用的边。

  • 最小割

1.几个定义

  • 割: 又称s-t割,是一种点的划分方式,将所有点分为两个集合ST,其中sStT

  • 割的容量: 定义为所有从S中的点出发到达T中的点的边的容量之和,即:c(S,T)=uS,vTc(u,v)

  • 最小割: 求出一个割使得c(S,T) 最小。

2.最大流最小割定理

在任何网络中,最大流的值等于最小割的容量。

posted @   Mcggvc  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示