网络流的概念入门第一讲

转载文章

查看资料:lrj 《算法竞赛入门经典》

相关概念:

最大流:(Maximum-Flow Problem)

      从源点 S  中间经过一些点,一些的物品运送到汇点 t 。

              中途每两点间都有个最大运送物品数。

              求从 s 到 t 最多能运送多少物品。

                                           

 

 

容量: 对于一条边 (u,v),它的物品上限(能够运送的物品最大数量)称为容量 (capacity),

    记为 c(u,v) (对于不存在的边 (u,v) , c(u,v) = 0)

流量: 实际运送物品数称为流量 (flow)

            规定:f(u,v) 和 f(v,u) 最多只有一个正数(可以均为 0),且 f(u,v) = - f(v,u)

 

                                      

PS:此图左边表示实际运送物品,右边表示最大容量。

 

结论:对于除了 s 和 t 的任意节点 u,  ∑ f(u,v)  = 0 (有些 f 为负数) 。

                                                           (u,v)∈E

 

最大流问题中: 容量 c 和 流量 f 满足三个性质

  容量限制 f(u,v) <= c(u,v)

  斜对称:f(u, v) = -f(u,v)

  流量平衡:对于除了 s 和 t 的任意节点 u,  ∑ f(u,v)  = 0 (有些 f 为负数) 。

                                                                                            (u,v)∈E

目标:最大化 | f |  = ∑ f(s,v)       =    ∑ f(u,t)             即从 S 点流出的净流量(=流入 t 点的净流量) 

                                 (s,v)∈E ,         (u,t)∈E

 

增广路算法:

残量:上图中每条边上的容量差 (称为残余流量,简称残量),

           比如说上面第二个图中 V2 到 V4 残量为 14-11 = 3; V4 到 V2 残量为 0-(-11)= 11

算法基于事实:

           残量网络中任何一个从 s 到 t 的有向道路都对应一条原图中的增广路【PS:不理解这个名词也没事继续看】。

           只要求出该道路中所有残量的最小值 d,把对应的所有边上的流量增加 d 即可,这个过程称为增广。

  也就是说只要有从起点 s 到终点 t 的路上存在流量,那么找出最小的残余流量 d

           那么这个 d 肯定是满足这条路径的每一条边的,否则找不出这样的 d

           那么这条路径上的每一条边的流量增加 d ,总流量增加 d 就好了。

           然后继续找,直到找不到为止。

  

不难证明如果增广前的流量满足 3 个条件,那么增广之后任然满足。

显然只要残量网中存在增广路,流量就可以增大。

逆命题:如果残量网中不存在增广路,则当前流就是最大流,这就是著名的增广路定理。

 

问题:如何找路径? DFS ms 很慢,用 BFS

 1 queue<int> q;
 2 memset(flow,0,sizeof(flow)); //初始化流量为 0
 3 f = 0; // 初始化总流量为 0
 4 for(;;) //BFS 找增广路
 5 {
 6     memset(a,0,sizeof(a)); // a[i]:从起点 s 到 i 的最小残量【每次for()时 a[] 重新清 0 因此同时可做标记数组 vis】
 7     a[s] = INF; //起点残量无线大
 8     q.push(s);  //起点入队
 9     while(!q.empty()) // BFS 找增广路
10     {
11         int u = q.front(); //取队首
12         q.pop(); // 出队
13         for(int v = 1; v <= n; v++) if(!a[v] && cap[u][v] > flow[u][v]) //找新节点 v
14         {
15             p[v] = u; q.push(v); //记录 v 的父亲节点,并加入 FIFO 队列
16             a[v] = min(a[u], cap[u][v]-flow[u][v]); // s-v 路径上的最小残量【从而保证了最后,每条路都满足a[t]】
17         }
18     }
19 
20     if(a[t] == 0) break; // 找不到,则当前流已经是最大流 【t为终点】
21 
22     for(int u = t; u != s; u = p[u]) // 从汇点往回走
23     {
24         flow[p[u]][u] += a[t]; // 更新正向流
25         flow[u][p[u]] -= a[t]; // 更新反向流
26     }
27     f += a[t]; // 更新从 S 流出的总流量
28 }

 

posted on 2017-01-09 12:56  清老师  阅读(266)  评论(0编辑  收藏  举报