最大流
问题:在一个流网络里,每条边都有运载能力限制,我最多能从源头运输多少数量到目的地?
网络流
定义
G=(V,E)是一个有向图,其中每条边(u,v)有一个非负的容量值c(u,v),而且如果E中包含一条边(u,v),那么图中就不存在它的反向边。在流网络中有两个特殊的结点,源结点s和汇点t。
下面给出流网络的形式化定义。令G=(V,E)为一个流网络,其容量函数为c,设s我为网络的源点,t为汇点。G中的流是一个实值函数f,满足以下两条性质:
-
容量限制(capacity contraint):对于所有的结点u,v,要求
-
流量守恒(flow conservation):对于所有的非源点和汇点的结点u,要求:
如下为一个流网络示意图:
残存网络
假定有一个流网络G =(V,E),其源点为s,汇点为t,f为G中的一个流。对即诶点对u,v,定义残存容量(residual capacity),有:
残存网络可能包含图G中不存在的边,残存网络中的反向边允许算法将已经发送出来的流量发送回去。一个残存网络示例图如下:
图a是一个流网络,b是a对应的残存网络,将权重为0的边省略。
残存网络是如何增大原始流网络中的流的一种指示。如果f是G的一个流,对应的有一个残存网络,残存网络中我们可以定义一个流f’。定义函数f↑f‘,我们将其称作流f’对f的增量(augmentation)。其值 = 流量 + 残存容量 - 反向残存容量。
增广路径
给定流网络G和流f,增广路径p是残存网络中一条从源结点s到汇点t的简单路径。根据残存网络的定义,对于一条增广路径上的边(u,v),我们可以增加其流量的幅度最大为,即我们之前定义的残存容量(residual capacity)。
最大流最小分割
-
割:是一种点的划分方式,对于一个网络流图 G=(V,E),设 E’ ⊆ E,若将 E’ 在 G 中删除后,使得图 G 不在连通,则称 E’ 是图 G 的割。割将所有的点划分为 S 和 T=V-S 两部分,记作:(S,T)
-
s-t 割:如果割所划分的两个子集使得源点 s∈S,汇点 t∈T,则该割称为 s-t 割,其中弧<u,v>|u∈S,v∈T 称为割的前向弧,弧 <u,v>| u∈T,v∈S 称为割的反向弧。
-
割的容量:所有前向弧的容量和,即起点在 S 终点在 T 中的所有边的容量和,记作:c(S,T) = Σc(u,v) | u∈S,v∈T
-
最小割:容量网络的最小割即容量最小的割。
-
净流量:对于一个割 (S,T),用净流量来表示穿过割 (S,T) 的流量之和,即:|f| = f(S,T) = Σf(u,v) | u∈S,v∈T
Ford-Fulkerson算法
Ford-Fulkerson算法就是在残存网络中不断找增广路径,并将增广路径纳入到结果中;重复这个过程直到无法再找到增广路径位置。

伪代码
#include<stdio.h>
#include<stdlib.h>
#include<vector>
#include<algorithm>
#define MAXVEX 100
#define INF 65535
//用于表示边的结构体
struct edge
{
int to;//终点
int cap;//容量
int rev;//反向边
};
std::vector<edge>G[MAXVEX];//图的邻接表表示
bool used[MAXVEX];//DFS中用到的访问标记
//向图中增加一条从s到t容量为cap的边
void addEdge(int from, int to, int cap)
{
edge e;
e.cap = cap;e.to = to;e.rev = G[to].size();
G[from].push_back(e);
e.to = from; e.cap = 0; e.rev = G[from].size() - 1;
G[to].push_back(e);
}
//通过DFS寻找增广路
int dfs(int v, int t, int f)
{
if (v == t)return f;
used[v] = true;
for (int i = 0; i < G[v].size(); ++i)
{
edge &e = G[v][i];
if (!used[e.to] && e.cap > 0)
{
int d = dfs(e.to, t, std::min(f, e.cap)