1、网络
网络,一种特殊的有向图,一个有向带权图 $G = (V,E)$,指定两个节点 $s$ 和 $t$($s \ne t$),分别称为源点和汇点,每条边的权值称为容量 $c$,特别地,若边 $\left\langle {u,v} \right\rangle \notin E$,则 $c\left( {u,v} \right) = 0$。
2、流
网络中的流是一个实值函数 $f:V \times V \to R$,满足下列性质:
(1)容量限制(Capacity Constraints):对于 $\forall u,v \in V$,要求 $f\left( {u,v} \right) \le c\left( {u,v} \right)$。通俗地讲,一条边的流量不能超过它的容量。
(2)反对称性(Skew Symmetry):对于 $\forall u,v \in V$,要求 $f\left( {u,v} \right) = -f\left( {u,v} \right)$。注意 $f(u,v)$ 是由 $u$ 到 $v$ 的净流,举例,如果由 $u$ 到 $v$ 有 $4$ 单位的实际流量以及由 $v$ 到 $u$ 有 $3$ 单位的实际流量,那么 $f(u,v) = 1$ 及 $f(v,u) = -1$。通俗地讲,一条边的净流量是有方向的,若方向与边的方向同向记为正,否则记为负。(根据本性质,不难得到 $f(u,u) = - f(u,u) \Rightarrow f(u,u) = 0$)
(3)流量守恒(Flow Conservation):对于 $\forall u \in V - \left\{ {s,t} \right\}$,要求 $\sum\limits_{v \in V} {f\left( {u,v} \right)} = 0$。通俗地讲,图上除源汇点外的任意一个节点,必须满足流入的流量等于流出的流量。
把流 $f$ 的值定义为 $\left| f \right| = \sum\limits_{v \in V} {f\left( {s,v} \right)}$,也就是说,从源点流出的全部流量之和(同样也等于流进汇点的全部流量之和),称为流的流值。
最大流就是所有流中流值最大的流。
设集合 $X,Y \subseteq V$,记 $f\left( {X,Y} \right) = \sum\limits_{u \in X} {\sum\limits_{v \in Y} {f\left( {u,v} \right)} }$
定理:设 $f$ 是网络 $G=(V,E)$ 中的一个流,那么
(1)对于 $\forall X \subseteq V$,有 $f\left( {X,X} \right) = 0$。
(2)对于 $\forall X,Y \subseteq V$,有 $f\left( {X,Y} \right) = - f\left( {Y,X} \right)$。
(3)对于 $\forall X,Y,Z \subseteq V$,其中 $X \cap Y = \emptyset$,有 $f\left( {X \cup Y,Z} \right) = f\left( {X,Z} \right) + f\left( {Y,Z} \right)$。
证明(2):
$\begin{array}{l} f\left( {X,Y} \right) \\ = \sum\limits_{u \in X} {\sum\limits_{v \in Y} {f\left( {u,v} \right)} } \\ = \left[ {f\left( {x_1 ,y_1 } \right) + \cdots + f\left( {x_1 ,y_n } \right)} \right] + \cdots + \left[ {f\left( {x_n ,y_1 } \right) + \cdots + f\left( {x_n ,y_n } \right)} \right] \\ = \left[ { - f\left( {y_1 ,x_1 } \right) - \cdots - f\left( {y_n ,x_1 } \right)} \right] + \cdots + \left[ { - f\left( {y_1 ,x_n } \right) - \cdots - f\left( {y_n ,x_n } \right)} \right] \\ = - \left[ {f\left( {y_1 ,x_1 } \right) + \cdots + f\left( {y_n ,x_1 } \right)} \right] - \cdots - \left[ {f\left( {y_1 ,x_n } \right) + \cdots + f\left( {y_n ,x_n } \right)} \right] \\ = - \left\{ {\left[ {f\left( {y_1 ,x_1 } \right) + \cdots + f\left( {y_n ,x_1 } \right)} \right] + \cdots + \left[ {f\left( {y_1 ,x_n } \right) + \cdots + f\left( {y_n ,x_n } \right)} \right]} \right\} \\ = - \sum\limits_{u \in Y} {\sum\limits_{v \in X} {f\left( {u,v} \right)} } \\ = - f\left( {Y,X} \right) \\ \end{array}$
证明(1):
由(2)可得 $f\left( {X,X} \right) = - f\left( {X,X} \right) \Rightarrow f\left( {X,X} \right) = 0$。
证明(3):
假设 $X = \left\{ {x_1 , \cdots ,x_n } \right\},Y = \left\{ {y_1 , \cdots ,y_n } \right\}$,由于 $X \cap Y = \emptyset$,
所以 $X \cup Y = \left\{ {x_1 , \cdots ,x_n ,y_1 , \cdots ,y_n } \right\}$,
因此不难得到
$\begin{array}{l} f\left( {X \cup Y,Z} \right) \\ = \left[ {f\left( {x_1 ,z_1 } \right) + \cdots + f\left( {x_1 ,z_n } \right)} \right] + \cdots + \left[ {f\left( {x_n ,z_1 } \right) + \cdots + f\left( {x_n ,z_n } \right)} \right] + \left[ {f\left( {y_1 ,z_1 } \right) + \cdots + f\left( {y_1 ,z_n } \right)} \right] + \cdots + \left[ {f\left( {y_n ,z_1 } \right) + \cdots + f\left( {y_n ,z = _n } \right)} \right] \\ = \sum\limits_{u \in X} {\sum\limits_{v \in Z} {f\left( {u,v} \right)} } + \sum\limits_{u \in Y} {\sum\limits_{v \in Z} {f\left( {u,v} \right)} } \\ = f\left( {X,Z} \right) + f\left( {Y,Z} \right) \\ \end{array}$
不难发现,这三个定理都是比较基础的性质推导,而它们都是有比较具象的意义的,那就是网络中的若干个节点,可以缩成一个节点,不影响网络的性质。
3、残留网络与增广路
对于网络 $G=(V,E)$,设流 $f$ 是 $G$ 中的流,对于 $G$ 中的每条边 $\left\langle {u,v} \right\rangle$,定义残留容量为在不超过容量限制的条件下,可以通过的额外的流量
$c_f \left( {u,v} \right) = c\left( {u,v} \right) - f\left( {u,v} \right)$
残留网络的定义:对于网络 $G=(V,E)$ 与流 $f$,残留网络为 $G_f = (V,E_f)$,其中边集 $E_f = \left\{ {\left( {u,v} \right) \in V \times V|c_f \left( {u,v} \right) > 0} \right\}$。
事实上,残留网络也是一个网络,原网络的一个流和对应的残留网络中的流相加依然是原网络中的一个流。
增广路 $p$ 为残留网络 $G_f$ 上的源点 $s$ 到汇点 $t$ 的一条简单路径,该路径的残留容量为可以沿该路径增加的最多额外流量 $c_f = \min \left\{ {c_f \left( {u,v} \right)|\left\langle {u,v} \right\rangle \in p} \right\}$,
由于我们知道残留网络上的所有容量必须是大于零的,所以增广路的残留容量也是严格大于零的。
增广路的可增广性质:定义流 $f_p$ 为
$f_p = \left\{ {\begin{array}{*{20}c} {c_f \left( p \right),\left\langle {u,v} \right\rangle \in p} \hfill \\ { - c_f \left( p \right),\left\langle {u,v} \right\rangle \in p} \hfill \\ {0,} \hfill \\ \end{array}} \right.$
则给定一个网络 $G$ 的一个流 $f$,则 $f+f_p$ 依然是网络 $G$ 的一个流。
4、最小割最大流定理
网络 $G=(V,E)$ 中,一个割 $[S,T]$ 将点集 $V$ 划分成两个部分,使得 $s \in S,t \in T$。符号 $[S,T]$ 代表边集 $\left\{ {\left\langle {u,v} \right\rangle |\left\langle {u,v} \right\rangle \in E,u \in S,v \in T} \right\}$。
穿过割 $[S,T]$ 的净流定义为 $f\left( {S,T} \right)$,割 $[S,T]$ 的容量定义为 $c\left( {S,T} \right)$,一个网络的最小割也就是该网络中的容量最小的割。
最小割最大流定理:设流 $f$ 为网络 $G=(V,E)$ 中的一个流,则
$f$ 是 $G$ 的一个最大流 $\Leftrightarrow$ $\left| f \right| = f\left[ {S,T} \right]$
且 $\left| f \right| \le c\left[ {S,T} \right]$。
5、Edmonds-Karp算法
待更新……
6、Dinic算法
时间复杂度 $O(n^2 m)$,而且实际应用中远远达不到该上界,一般可以处理 $10^4$ 到 $10^5$ 规模的网络。
Dinic算法求二分图最大匹配的时间复杂度为 $O(m \sqrt n)$,实际表现则更快。
算法步骤:
1、在残留网络上BFS求出每个节点的深度,构造分层图。
2、在分层图上DFS寻找增广路,在回溯时实时更新剩余容量。另外,每个点可以流向多条出边,同时加入若干剪枝。
算法模板:
#include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int INF=0x3f3f3f3f; const int maxn=5e4+10; const int maxm=3e5+10; struct Edge{ int u,v,c,f; }; struct Dinic { int s,t; //源点汇点 vector<Edge> E; vector<int> G[maxn]; void init(int l,int r) { E.clear(); for(int i=l;i<=r;i++) G[i].clear(); } void addedge(int from,int to,int cap) { E.push_back((Edge){from,to,cap,0}); E.push_back((Edge){to,from,0,0}); G[from].push_back(E.size()-2); G[to].push_back(E.size()-1); } int dist[maxn],vis[maxn]; queue<int> q; bool bfs() //在残量网络上构造分层图 { memset(vis,0,sizeof(vis)); while(!q.empty()) q.pop(); q.push(s); dist[s]=0; vis[s]=1; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=0;i<G[now].size();i++) { Edge& e=E[G[now][i]]; int nxt=e.v; if(!vis[nxt] && e.c>e.f) { dist[nxt]=dist[now]+1; q.push(nxt); vis[nxt]=1; } } } return vis[t]; } int dfs(int now,int flow) { if(now==t || flow==0) return flow; int rest=flow,k; for(int i=0;rest>0 && i<G[now].size();i++) { Edge &e=E[G[now][i]]; int nxt=e.v; if(e.c>e.f && dist[nxt]==dist[now]+1) { k=dfs(nxt,min(rest,e.c-e.f)); if(!k) dist[nxt]=0; //剪枝,去增广完毕的点 e.f+=k; E[G[now][i]^1].f-=k; rest-=k; } } return flow-rest; } int mf; //存储最大流 int maxflow() { mf=0; int flow=0; while(bfs()) while(flow=dfs(s,INF)) mf+=flow; return mf; } }dinic; int n,m; int main() { while(scanf("%d%d",&m,&n)!=EOF) { dinic.init(1,n); for(int from,to,cap,i=1;i<=m;i++) { scanf("%d%d%d",&from,&to,&cap); dinic.addedge(from,to,cap); } dinic.s=1,dinic.t=n; dinic.maxflow(); printf("%d\n",dinic.mf); } }
7、最小费用最大流的Edmonds-Karp算法
待更新……