一、综述
1.定义
定义1:流网络
定义2:残留容量
定义3:增广路径
已知一个网络流G=(V,E)和流f,增广路径p为残留网络G|f中从s到t的一条简单路径
能够沿一条增广路径p的每条边传输的网络流的最大量为p的残留容量,由下式定义:
c|f(p) = min{c|f(u,v) : (u,v)在p上}
定义4:割、净流、容量、最小割
净流和容量的区别:
穿过(S,T)的净流由双向的正网络流组成;在加上从S到T的正网络流的同时,减去从T到S的正网络流。
割(S,T)的容量仅由从S到T的连计算而得。从T到S的边在计算c(S,T)时是不算在内的。
2.性质
3.定理
定理1:
定理2:
定理3:
定理4:
定理5:
定理6:
对一个网络流G中任意流f来说,其值的上界面为G的任意割的容量
定理7:
二、代码
版本一:最大流,图用矩阵实现,求增广路径用BELLMAN-FORD实现
1.Mat_Flow.h
#include <iostream> using namespace std; #define NMAX 210 class Mat_Flow { public: int n;//点的个数。其中0是源点,1是汇点 int map[NMAX][NMAX];//网络费用 int net[NMAX][NMAX];//剩余网络 int path[NMAX];//增广路径,path[v]=u说明(u,v)在增广路径上 int ecost[NMAX];//源点到各点的最短路径 Mat_Flow(int num):n(num) { memset(map, 0, sizeof(map)); } void AddSingleEdge(int start, int end, int value = 1) { map[start][end] = value; } void MakeGraph(int m); bool bellman_ford(); int max_flow(); }; void Mat_Flow::MakeGraph(int m) { int start, end, value; while(m--) { cin>>start>>end>>value; AddSingleEdge(start, end, value); } } bool Mat_Flow::bellman_ford() { int i, j; memset(path, -1, sizeof(path)); fill(ecost, ecost+NMAX, INT_MAX); ecost[0] = 0; bool flag = true; while(flag) { flag = false; for(i = 0; i <= n; i++) { if(ecost[i] == INT_MAX) continue; for(j = 0; j <= n; j++) { if(net[i][j] > 0 && ecost[i] + 1 < ecost[j]) { flag = true; ecost[j] = ecost[i] + 1; path[j] = i; } } } } return ecost[n] != INT_MAX; } int Mat_Flow::max_flow() { int i, j; //初始时,剩余网络即为整个网络 for(i = 0; i <= n; i++) { for(j = 0; j <= n; j++) net[i][j] = map[i][j]; } int maxflow = 0; //while there exists a path p from s to t int the residual network G1 //从剩余网络中找到一条增广路径,增广路径存在在path中 while(bellman_ford()) { //do c|f(p) <- min {c|f(u,v):(u,v) is in p} //计算增广路径上的净流 int v = n, cfp = INT_MAX, u; while(v != 0) { //path存储的是增广路径,path[v]=u说明(u,v)在增广路径上 u = path[v]; cfp = min(cfp, net[u][v]); v = u; } //更新最大流的大小 maxflow += cfp; //更新剩余网络 //for each edge(u,v) in p v = n; while(v != 0) { u = path[v]; //f[u,v] <- f[u,v] + cfp net[u][v] -= cfp; net[v][u] += cfp; //f[v,u] <- -f[u,v] v = u; } } return maxflow; }
2.main.cpp
#include "Mat_flow.h" /* 5 10 0 1 16 0 2 13 1 3 12 1 2 10 2 1 4 3 2 9 2 4 14 3 5 20 4 3 7 4 5 4 */ int main() { int n, m; while(cin>>n>>m) { Mat_Flow *G = new Mat_Flow(n); G->MakeGraph(m); cout<<G->max_flow()<<endl; delete G; } return 0; }
3.测试数据
《算法导论》P405图26-5
4.运行结果
版本2:矩阵+HLPP(高度标号预流推进算法)
1."Mat_HLPP_Flow.h"
http://my.csdn.net/my/code/detail/50632
2.main.cpp
#include "Mat_HLPP_Flow.h" /* 5 10 0 1 16 0 2 13 1 3 12 1 2 10 2 1 4 3 2 9 2 4 14 3 5 20 4 3 7 4 5 4 */ int main() { int n, m; while(cin>>n>>m) { Mat_HLPP_Flow *G = new Mat_HLPP_Flow(n); G->MakeGraph(m); cout<<G->high_label_preflow_push()<<endl; delete G; } return 0; }
3.测试数据与测试结果
同上
三、练习
26.1 网络流
26.1-1
定义1:如果(u,v)不属于E,c(u,v)=0 性质1:f(u,v) <= c(u,v) ====> 如果(u,v)不属于E,f(u,v) = 0。 反向边同理
26.1-2
性质2,反对称性
26.1-3
待解决
26.1-4
26.1-5
(1)f(X,Y)=-f(V-X,Y) X:t Y:v3,v4 (2)f(X,Y)!=-f(V-X,Y) X:v3,v4 Y:v1,v2
26.1-6
必定满足“反对称性”和“流守恒性”,可能违反“容量限制”
26.1-7
由26.1-6知,af1+(1-a)f2满足“反对称性”和“流守恒性”。 因为f1和f2满足“容量限制”,因此af1+(1-a)f2满足“容量限制” 所以af1+(1-a)f2也是流
26.1-8
在网络中每一节点流入流出的量应该相等
例如上图,可以写出以下等式:
x1 – x3 –x4 = 0
x2 + x3 – x5 = 0
26.1-9
将地图转换为一个有向图: (1)每个角落作为一个顶点 (2)若一个角落到另一个角落有路,则构成有向图的边。 (3)每条路构成正向和反向两条边,容量都是1 计算该有向图的最大流,若最大流大于或等于2,则“可以”
26.2Ford-Fulkerson方法
26.2-1
净流:19
容量:31
26.2-2
26.2-3 最大流的最小割是23 第二条增广路径的第二段和第三条增广路径的第2段抵消
第一条增广路径的第三段和第四条增广路径的第2段抵消 26.2-4 根据反对称性和残留网络定义
c|f(u,v) + c|f(v,u) = c(u,v) - f(u,v) + c(v,u) - f(v,u) = c(u,v) + c(v,u) 26.2-5
根据流守恒性
26.2-6
26.2-7
似乎是很显然的事情
26.2-8
不知道题目是什么意思,讨厌证明题
26.2-9
构造这样一个带权有向图G2,
G2的顶点和G是一样的。若G中存在一条(u,v)的边,则在G2中加一条u->v的边,和一条v->u的边,权值都是1.
在G2的基础上构造|V|个网络流,依次令|V|个顶点分别做为汇点,再选一个不是汇点的点做为源点。依次求这|V|个网络流的最大流。
这|V|个网络流的最小值即为G的边连通度
26.3最大二分匹配
26.3-1
26.3-2
又见证明题
26.3-3
2*min(|L|, |R|)+1
26.4 压入与重标记法
26.5重标记与前移算法
四、思考题
26-1逃脱问题
(1)构造网络流G,令m个点为G的源点,令边界点中不是源点的点为G的汇点。
(2)若两个顶点相邻,则构造两条有向边,边权为1