网络流初步
一、网络流的定义
有向图 G = ( V, E )中:
•有唯一的一个源点S(入度为0:出发点)
•有唯一的一个汇点 T(出度为0:结束点)
•图中每条弧 (u, v) 都有一非负容量 c ( u, v )
满足上述条件的图G称为网络流图。
记为: G = ( V, E ,C)
1、可行流
◆每条弧 ( u, v )上 给定一个实数f(u,v),满足:有 0 <= f ( u, v ) <= c( u, v ),则f(u,v)称为弧( u, v )上的流量。
◆如果有一组流量满足条件:
源点s : 流出量 = 整个网络的流量
汇点t : 流入量 =整个网络的流量
中间点:总流入量 = 总流出量
那么整个网络中的流量成为一个可行流。
2、最大流
•在所有的可行流中, 流量最大的一个流的流量
如: 图2中可行流7也是最大流。
•最大流可能不只一个。
二、最大流算法
◆Ford-Fulkerson (福特-福克森)算法:
步骤:
(1)如果存在增广路径,就找出一条增广路径
(1)如果存在增广路径,就找出一条增广路径
(2)然后沿该条增广路径进行更新流量(增加流量)
1、增广路径
•从 s 到 t 的一条简单路径,若边 ( u, v ) 的方向与该路径的方向一致,称 ( u, v ) 为正向边,方向不一致时称为逆向边。
若路径上所有的边满足:
①所有正向边有:f ( u, v ) < c ( u, v)
②所有逆向边有:f ( u, v ) > 0
②所有逆向边有:f ( u, v ) > 0
则称该路径为一条增广路径(可增加流量)
2、沿增广路径增广
1) 先设d为为正无穷(可增加流,余流量)
若( u, v ) 是正向边
d = min ( d, c ( u, v ) – f (u, v ) )
若( u, v ) 是逆向边
d = min ( d, f ( u, v ) )
2) 对与该增广路径上的边
若( u, v ) 是正向边,f ( u, v ) = f ( u, v ) + d;
若( u, v ) 是逆向边,f ( u, v ) = f ( u, v ) – d;
增广后,总流量增加了d
•定理:
可行流 f 为最大流,当且仅当不存在关于f 的增广路径
证:若 f 是最大流,但图中存在关于 f 的增广路径,则可以沿该增广路径增广,得到的是一个更大的流,与f 是最大流矛盾。所以,最大流不存在增广路径。
◆Ford-Fulkerson方法(增广流)求最大流
(福特-福克森)
(福特-福克森)
步骤:
(1)如果存在增广路径,就找出一条增广路径
(1)如果存在增广路径,就找出一条增广路径
DFS,BFS
(2)然后沿该条增广路径进行更新流量
(2)然后沿该条增广路径进行更新流量
(增加流量)
While 有增广路径 do 更新该路径的流量
迭代算法
算法的实现:
c [ u, v ]:容量
f [ u, v ]:流量
B[i]:保存找到的增广路径,记录路径上结点i的前驱结点。
Sum:最大流量。
假定:1是源点S;n是汇点T。
#include <iostream> #include <algorithm> #include <cstring> #define OO 999999 using namespace std; int c[100][100];//容量 int f[100][100];//流量 int b[100]; //增广路径前驱结点 int sum; int n,m; bool findflow(int k); void addflow(); //找到k节点的后继节点i bool findflow(int k) { if (k==n) return true;//找到了一条增广路径 for (int i=1;i<=n;i++) { if ( b[i]==-1 && (c[k][i]-f[k][i]>0||f[i][k]>0) ) { b[i]=k; if ( findflow(i) ) return true; } } return false; } //沿增广路径增广 void addflow() { int d=OO; int i=n; while ( b[i]!=0 ) { if ( c[b[i]][i]>0 ) { d=min(d,c[b[i]][i]-f[b[i]][i]); } if ( c[i][b[i]]>0 ) { d=min(d,f[i][b[i]]); } i=b[i]; } i=n; while ( b[i]!=0 ) { if ( c[b[i]][i]>0 ) { f[b[i]][i]+=d; } if ( c[i][b[i]]>0 ) { f[i][b[i]]-=d; } i=b[i]; } sum+=d; } int main() { int x,y,z; cin >> n >> m; memset(c,-1,sizeof(c)); memset(f,0,sizeof(f)); for (int i=1;i<=m;i++) { cin >> x >> y >> z; c[x][y]=z; } for (int i=0;i<=n;i++) b[i]=-1; b[1]=0; while ( findflow(1) ) { addflow(); for (int i=0;i<=n;i++) b[i]=-1; b[1]=0; } cout << sum << endl; for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) { if (f[i][j]>0) cout << i << "-->" << j << " " << f[i][j] << endl; } } return 0; }