网络流算法学习笔记——Dinic有效算法

2023-05-16

屈婉玲《算法设计与分析》第2版第7章网络流算法学习笔记。

概述

Dinic有效算法同样用来求最大流,相当于FF算法的改进。

FF算法参考:网络流算法学习笔记——最大流问题基本概念和Ford-Fulkerson方法(标号法C++实现)

改进之处有:

  1. FF算法每次只找1条s-t增广链,而Dinic算法每次能找很多条
  2. Dinic算法每次都找最短的(边数最少的)s-t增广链(why?)

第1点显然能提高效率,但第2点为什么要找最短路径呢?看《算法导论》的一个FF算法的例子。

初始为0流的网络,找到一条增广路径:
在这里插入图片描述
更新残留网络,再找增广路径:
在这里插入图片描述
更新残留网络:
在这里插入图片描述
在这种情况下,每次仅增加1个单位的总流量,共要执行2,000,000次运算。

dinic算法能确保不发生这种情况。

概念

初始网络N,及当前可行流f:
在这里插入图片描述
辅助网络、辅助容量:N(f)、ac
设容量网络N,f是一个可行流,记其辅助网络为N(f);屈婉玲版《算法设计与分析》对残留网络的别称。

辅助容量是辅助网络中重新定义的容量 :

  • ac(i, j) = c(i, j) - f(i, j),当<i, j>是前向边,且f(i, j) < c(i, j)时
  • ac(j, i) = f(i, j),当<i, j>是前向边,且f(i, j) > 0时

即前向边还能压入多少流量,后向边还能退回多少流量。

分层辅助网络:AN(f)
删除辅助网络中部分非最短路径的辅助网络。用广度优先搜索N(f),标记每个节点的层号(图b方框里标注),把从高层到低层的边,及层数大于等于t的顶点删掉。

在这里插入图片描述
在这里插入图片描述
流通量:th(i)
AN(f)中所有以顶点i为终点的边的容量和,与所有以i为始点的容量和中的最小者。即顶点i所能承载的最大流量。规定以s为终点和以t为始点的边的容量为+∞。

算法步骤

1. 构造AN(f)

接下来,循环执行下面的步骤2、3。

2. 删去流通量0的顶点和边

流通量为0的顶点及关联的边都可以删去,出现新的流通量0的顶点继续删:
在这里插入图片描述
至此,网络简化了不少,准备工作完成了。

3. 安排流量

构造好AN(f)后,尽可能多的找出s到t的最短路径并分配流量:

  1. 找出流通量最小的顶点:1,6,10,流通量均为3;
  2. 从顶点1向后看,发出3单位的流量,尽可能多的分配给每个后面的边(比如从顶点4分配2个单位的流量时,不能给顶点6一个、给顶点8一个),全部送到t为止;
  3. 从顶点1向前看,从s获得3单位的流量。

在这里插入图片描述

安排完毕后,重复步骤2,AN(f)变成了下图;重复步骤3安排流量:
在这里插入图片描述
再重复步骤2,发现此时s、t的流通量都变为了0(下图所有顶点流通量都是0),跳出循环:
在这里插入图片描述
至此,把以上找到的流叠加起来,就找到了AN(f)的一个极大流g1,如下:
在这里插入图片描述
将极大流叠加到原来的流f上,得到新的N和f2:
在这里插入图片描述


至此第一个过程结束。回到步骤1,重复执行上述过程,构造AN(f)并寻找其极大流:
在这里插入图片描述
在这里插入图片描述

下一组,构造AN(f)时发现没有从s到t的通路,计算结束:
在这里插入图片描述

代码实现

伪码

屈婉玲《算法设计与分析》第2版中的伪代码如下:

1. f ← 0  //取零流作为初始可行流
2. 构造AN(f)  //bfs遍历
3. if AN(f)中不存在从s到t的路径 then return f  //计算结束
4. g ← 0
5. if AN(f)中存在th(i)=0 then
6.     if i=s or i=t then goto 15
7.     else 删去i及其关联的边
8. 找到流通量最小的顶点k,从k开始将th(k)个单位的流量向后送到t,向前推到s,并将它们加到g上
9. 删去k及其关联的边
10. for <i, j> ∈ AE(f) do
11.     if g(i, j)=ac(i, j) then
12.         删去<i, j>
13.     else ac(i, j)←ac(i, j) - g(i, j)
14. goto 5
15. f ← f + g
16. goto 2

唯一没明确方法的是第8步,如何安排流量?可以采用dfs的方法,从k分别向后、向前dfs。

下面分析复杂度。AN(f)中s-t的距离每个阶段至少增加1(每次都找最短的),所以至多有n个阶段。每个阶段中:

  1. 构造AN(f)需O(m);
  2. 生成g的过程中,找流通量最小的点需O(n),最多找n次,需O(n2);
  3. 删除边的操作需要O(m);
  4. 安排流量时,从流通量最下的点安排流至多有n条边,至多进行n次,需 O(n2);

所以总工作量为O(n3)。

C++实现

下面是简洁的教程邻接表版C++实现,来自:Dinic’s algorithm for Maximum Flow

#include<bits/stdc++.h> 
using namespace std; 
  
// A structure to represent a edge between 
// two vertex 
struct Edge 
{ 
    int v ;  // Vertex v (or "to" vertex) 
             // of a directed edge u-v. "From" 
             // vertex u can be obtained using 
             // index in adjacent array. 
  
    int flow ; // flow of data in edge 
  
    int C;    // capacity 
  
    int rev ; // To store index of reverse 
              // edge in adjacency list so that 
              // we can quickly find it. 
}; 
  
// Residual Graph 
class Graph 
{ 
    int V; // number of vertex 
    int *level ; // stores level of a node 
    vector< Edge > *adj; 
public : 
    Graph(int V) 
    { 
        adj = new vector<Edge>[V]; 
        this->V = V; 
        level = new int[V]; 
    } 
  
    // add edge to the graph 
    void addEdge(int u, int v, int C) 
    { 
        // Forward edge : 0 flow and C capacity 
        Edge a{v, 0, C, adj[v].size()}; 
  
        // Back edge : 0 flow and 0 capacity 
        Edge b{u, 0, 0, adj[u].size()}; 
  
        adj[u].push_back(a); 
        adj[v].push_back(b); // reverse edge 
    } 
  
    bool BFS(int s, int t); 
    int sendFlow(int s, int flow, int t, int ptr[]); 
    int DinicMaxflow(int s, int t); 
}; 
  
// Finds if more flow can be sent from s to t. 
// Also assigns levels to nodes. 
bool Graph::BFS(int s, int t) 
{ 
    for (int i = 0 ; i < V ; i++) 
        level[i] = -1; 
  
    level[s] = 0;  // Level of source vertex 
  
    // Create a queue, enqueue source vertex 
    // and mark source vertex as visited here 
    // level[] array works as visited array also. 
    list< int > q; 
    q.push_back(s); 
  
    vector<Edge>::iterator i ; 
    while (!q.empty()) 
    { 
        int u = q.front(); 
        q.pop_front(); 
        for (i = adj[u].begin(); i != adj[u].end(); i++) 
        { 
            Edge &e = *i; 
            if (level[e.v] < 0  && e.flow < e.C) 
            { 
                // Level of current vertex is, 
                // level of parent + 1 
                level[e.v] = level[u] + 1; 
  
                q.push_back(e.v); 
            } 
        } 
    } 
  
    // IF we can not reach to the sink we 
    // return false else true 
    return level[t] < 0 ? false : true ; 
} 
  
// A DFS based function to send flow after BFS has 
// figured out that there is a possible flow and 
// constructed levels. This function called multiple 
// times for a single call of BFS. 
// flow : Current flow send by parent function call 
// start[] : To keep track of next edge to be explored. 
//           start[i] stores  count of edges explored 
//           from i. 
//  u : Current vertex 
//  t : Sink 
int Graph::sendFlow(int u, int flow, int t, int start[]) 
{ 
    // Sink reached 
    if (u == t) 
        return flow; 
  
    // Traverse all adjacent edges one -by - one. 
    for (  ; start[u] < adj[u].size(); start[u]++) 
    { 
        // Pick next edge from adjacency list of u 
        Edge &e = adj[u][start[u]];  
                                      
        if (level[e.v] == level[u]+1 && e.flow < e.C) 
        { 
            // find minimum flow from u to t 
            int curr_flow = min(flow, e.C - e.flow); 
  
            int temp_flow = sendFlow(e.v, curr_flow, t, start); 
  
            // flow is greater than zero 
            if (temp_flow > 0) 
            { 
                // add flow  to current edge 
                e.flow += temp_flow; 
  
                // subtract flow from reverse edge 
                // of current edge 
                adj[e.v][e.rev].flow -= temp_flow; 
                return temp_flow; 
            } 
        } 
    } 
  
    return 0; 
} 
  
// Returns maximum flow in graph 
int Graph::DinicMaxflow(int s, int t) 
{ 
    // Corner case 
    if (s == t) 
        return -1; 
  
    int total = 0;  // Initialize result 
  
    // Augment the flow while there is path 
    // from source to sink 
    while (BFS(s, t) == true) 
    { 
        // store how many edges are visited 
        // from V { 0 to V } 
        int *start = new int[V+1]; 
  
        // while flow is not zero in graph from S to D 
        while (int flow = sendFlow(s, INT_MAX, t, start)) 
  
            // Add path flow to overall flow 
            total += flow; 
    } 
  
    // return maximum flow 
    return total; 
} 
  
// Driver program to test above functions 
int main() 
{ 
    Graph g(6); 
    g.addEdge(0, 1, 16 ); 
    g.addEdge(0, 2, 13 ); 
    g.addEdge(1, 2, 10 ); 
    g.addEdge(1, 3, 12 ); 
    g.addEdge(2, 1, 4 ); 
    g.addEdge(2, 4, 14); 
    g.addEdge(3, 2, 9 ); 
    g.addEdge(3, 5, 20 ); 
    g.addEdge(4, 3, 7 ); 
    g.addEdge(4, 5, 4); 
  
    // next exmp 
    /*g.addEdge(0, 1, 3 ); 
      g.addEdge(0, 2, 7 ) ; 
      g.addEdge(1, 3, 9); 
      g.addEdge(1, 4, 9 ); 
      g.addEdge(2, 1, 9 ); 
      g.addEdge(2, 4, 9); 
      g.addEdge(2, 5, 4); 
      g.addEdge(3, 5, 3); 
      g.addEdge(4, 5, 7 ); 
      g.addEdge(0, 4, 10); 
  
     // next exp 
     g.addEdge(0, 1, 10); 
     g.addEdge(0, 2, 10); 
     g.addEdge(1, 3, 4 ); 
     g.addEdge(1, 4, 8 ); 
     g.addEdge(1, 2, 2 ); 
     g.addEdge(2, 4, 9 ); 
     g.addEdge(3, 5, 10 ); 
     g.addEdge(4, 3, 6 ); 
     g.addEdge(4, 5, 10 ); */
  
    cout << "Maximum flow " << g.DinicMaxflow(0, 5); 
    return 0; 
} 

输出结果:

Maximum flow 23

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络流算法学习笔记——Dinic有效算法 的相关文章

随机推荐

  • 【EtherCAT实践篇】七、更改XML示例2,增加16位模拟输入

    目的 xff1a 在EtherCAT开发板上IO程序基础上增加一个16位的变量mytest xff0c 用于传输模拟量发送给主站 1 EtherCAT增加变量说明 在实际使用中 xff0c 可现有程序的输入输出变量可能不能直接满足实际需求
  • 【EtherCAT实践篇】八、更改XML示例3,增加16位模拟DAC输出

    目的 xff1a 在EtherCAT开发板上IO程序 xff08 基本IO通讯 基于SSC xff09 基础上进行修改 xff0c 增加一路模拟量输出 xff0c 并输出给DAC管脚 由于STM32F405底板具有DAC输出功能 xff0c
  • Pixhawk固件PX4之串口通讯

    1 目的 为进一步扩展pixhawk的接口及功能 xff0c 通过pixhawk现有接口 xff08 串口 I2C等 xff09 连接外部设备来实现 xff0c 本节内容主要介绍串口通讯方式 2 测试平台 硬件 xff1a pixhawk
  • 手机充电原理分析及问题总结

    xff08 1 xff09 充电流程介绍 xff1a 当充电器插入时 xff0c 亦即为PMIC充电模块提供了Vcharge电压 xff0c 这时会产生一个充电中断信号到CPU xff0c 通知CPU现在已经进入充电状态 CPU开始启动如下
  • 串口接收不定长数据的几种方法

    串口作为单片机开发的一个常用的外设 xff0c 应用范围非常广 大部分时候 xff0c 串口需要接收处理的数据长度是不定的 那么怎么才能判断一帧数据是否结束呢 xff0c 今天就以STM32单片机为例 xff0c 介绍几种接收不定长数据的方
  • 删除分节符的技巧

    WORD中删除分节符有这样的规定 xff1a 如果要删除分节符 xff0c 只要把光标移动到该分节符上 xff0c 按Delete键即可 但是要注意该分节符前面的文字将合并到后面的节中 xff0c 并且采用后者的格式设置 我就不知道天杀的微
  • 虚机创建异常报错No valid host was found,There are not enough hosts available

    虚机创建异常 xff0c 使用nova show 虚机ID提示fault报错信息 xff1a No valid host was found xff0c There are not enough hosts available 检查所在宿主
  • vuzzer 具体原理解析

    目录 1 安装 vmware 15 01环境下安装 xff1a 2 vuzzer使用说明 3 vuzzer原理 3 1权重文件以及有着cmp信息的文件生成 3 2 vuzzer种子生成 xff0c 变异原理 3 2 1 runfuzz py
  • C++ unordered_set

    目录 1 定义 2 基本的函数 2 1 unordered set构造 2 2 添加新的元素 注意无法插入相同元素 2 3 查找元素 2 4 查找桶接口 2 5 观察器 2 6 清除元素 2 7 其他函数 1 定义 unordered se
  • clang 10 介绍——sanitizerCoverage

    1 Introduction llvm内置了一个简单的代码覆盖率检测 xff08 sanitizercoverage xff09 它在函数级 基本块级和边缘级插入对用户定义函数的调用 提供了这些回调的默认实现 xff0c 并实现了简单的覆盖
  • C++ 匿名函数

    1 定义 所谓匿名函数 xff0c 其实类似于python中的lambda函数 xff0c 其实就是没有名字的函数 使用匿名函数 xff0c 可以免去函数的声明和定义 这样匿名函数仅在调用函数的时候才会创建函数对象 xff0c 而调用结束后
  • 给docker中的ubuntu系统安装桌面程序

    原本服务器是centos的 xff0c 用的不是很习惯 xff0c 也为了可以分割功能 xff0c 于是在服务器上装了docker xff0c docker里装了ubuntu系统 xff0c 具体过程可以参见https blog csdn
  • 陇剑杯 2021 write up整理

    竞赛 write up 收集和整理 陇剑杯 2021 write up整理1 签到题1 1 2 JWT2 12 22 32 42 52 6 3 webshell3 13 23 33 43 53 63 7 4 日志分析4 14 24 3 5
  • PBCTF2021

    PBCTF2021 1 MISC1 1 幽灵作家 Ghost Writer 2 Crypto2 1 Alkaloid Stream2 2 Steroid Stream2 3 good hash 1 MISC 1 1 幽灵作家 Ghost W
  • ASISCTF

    warm up 题目代码如下图所示 xff0c 我们会发现整个加密过程如下所示 xff0c 先在flag后面加上p长度的随机字符 xff0c 然后选择pow s i p 的结果选择字符重新连接字符串实现加密 usr bin env pyth
  • burpsuite简介

    1 burpsuite简介 burpsuite作为web渗透较为常用的软件 xff0c 有着9个比较常用的模块 proxy xff0c target xff0c intruder xff0c comparer xff0c repeater
  • libFuzzer

    目录 1 概述 2 版本 3 Fuzz Target 4 Fuzzer Usage 5 Corpus 6 Running 7 Parallel Fuzzing 8 Fork mode 9 Resuming merge 10 Options
  • linux virsh console无法登入虚拟机,宿主机virsh console 登录异常

    创建虚机后在宿主机上通过virsh console 登录不进去 xff0c 一直卡在登录界面 xff08 可以通过命名空间ssh登录 xff0c 排除用户名及密码问题 xff09 原因是相关配置文件没有添加 xff0c 可以通过以下方法进行
  • FrankMocap:A Monocular 3D Whole-Body Pose Estimation System via Regression and Integration 2021阅读理解

    ICCV Workshop 2021 Facebook AI research Btw why the name FrankMocap Our pipeline to integrate body and hand modules remi
  • 网络流算法学习笔记——Dinic有效算法

    屈婉玲 算法设计与分析 第2版第7章网络流算法学习笔记 概述 Dinic有效算法同样用来求最大流 xff0c 相当于FF算法的改进 FF算法参考 xff1a 网络流算法学习笔记 最大流问题基本概念和Ford Fulkerson方法 xff0