网络流的基本概念跟算法原理我是在以下两篇博客里看懂的,写的非常好。

http://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html

http://www.cnblogs.com/zsboy/archive/2013/01/27/2878810.html

网络流有四种算法, 包括 Edmond-Karp(简称EK), Ford-Fulkerson(简称FF), dinic算法以及SAP算法。

下面我会写出前三种算法的矩阵跟邻接表的形式, 对于第四种以后有必要再补充上, 其中dinic算法是比较高效的算法, 要重点掌握dinc,其他两种我是当做辅助理解网络流的,以后做题还是得练dinic

领接矩阵:适用于稠密图,原因:矩阵大小是 n * n的,若边m << n * n,则矩阵中有很多空位置,造成空间浪费, 所以只有稠密图才适合矩阵写法

邻接表:适用于稀疏图,原因:附加链域,稠密图不适合

附上例题链接:http://poj.org/problem?id=1273

1.Edmond-Karp

邻接矩阵:

  1. //邻接矩阵
  2.  
  3. #include<stdio.h>
  4. #include<string.h>
  5. #include<algorithm>
  6. #include<queue>
  7. #define mem(a, b) memset(a, b, sizeof(a))
  8. const int inf = 0x3f3f3f3f;
  9. using namespace std;
  10.  
  11. int m, n;//m为边的数量, n为点的数量
  12. int map[][];//存图
  13. int flow[]; //记录bfs查找时的最小边,类似木桶效应
  14. int pre[];
  15. queue<int>Q;
  16.  
  17. int bfs(int st, int ed)
  18. {
  19. while(!Q.empty())//队列清空
  20. Q.pop();
  21. for(int i = ; i <= n; i ++)//1.点的前驱, 利用前驱来更新路径上的正反向边的所剩容量 2.标记是否已经遍历过
  22. pre[i] = -;
  23. flow[st] = inf;//flow数组只需要每次bfs时将源点初始化为inf即可, 不需要全部初始化,因为更新时只跟上一个状态以及map边的信息有关
  24. Q.push(st);
  25. while(!Q.empty())
  26. {
  27. int index = Q.front();
  28. Q.pop();
  29. if(index == ed)//找到一条增广路径
  30. break;
  31. for(int i = ; i <= n; i ++)//遍历图
  32. {
  33. if(pre[i] == - && map[index][i] > && i != st)//1.没经过i点 2.边的容量大于0 3.终点不为起点,防止没经过汇点死循环
  34. {
  35. flow[i] = min(flow[index], map[index][i]);//找到一个可行流上最小的一条边
  36. pre[i] = index;//记录前驱
  37. Q.push(i);
  38. }
  39. }
  40. }
  41. if(pre[ed] == -)//汇点前驱没被更新说明没找到增广路径
  42. return -;
  43. else
  44. return flow[ed];
  45. }
  46.  
  47. int max_flow(int st, int ed)
  48. {
  49. int inc; //每次bfs查找得到的增量
  50. int ans = ; //记每次的增量之和为答案
  51. while((inc = bfs(st, ed)) != -)
  52. {
  53. int k = ed; //从汇点往回更新
  54. while(k != st)
  55. {
  56. int last = pre[k];
  57. map[last][k] -= inc;
  58. map[k][last] += inc;
  59. k = last;
  60. }
  61. ans += inc;
  62. }
  63. return ans;
  64. }
  65.  
  66. int main()
  67. {
  68. while(scanf("%d%d", &m, &n)!=EOF)
  69. {
  70. mem(map, );//图的边容量初始化为0
  71. for(int i = ; i <= m; i ++)
  72. {
  73. int a, b, c;
  74. scanf("%d%d%d", &a, &b, &c);
  75. if(a == b)
  76. continue;
  77. map[a][b] += c;//网络流单向边,若i到j有多根管子,可看作容量叠加的一根管子
  78. }
  79. int ans = max_flow(, n); //源点1到汇点n的最大流
  80. printf("%d\n", ans);
  81. }
  82. return ;
  83. }

邻接表(链式前向星实现)

  1. //链式前向星
  2.  
  3. #include<stdio.h>
  4. #include<string.h>
  5. #include<algorithm>
  6. #include<queue>
  7. #define mem(a, b) memset(a, b, sizeof(a))
  8. const int inf = 0x3f3f3f3f;
  9. using namespace std;
  10.  
  11. struct Edge
  12. {
  13. int next, to, val;
  14. }edge[ * ];//反向边 开两倍空间
  15.  
  16. int m, n;
  17. int head[], cnt, pos[];
  18. int flow[];
  19. int pre[];
  20. queue<int>Q;
  21.  
  22. void add(int a, int b, int c)
  23. {
  24. edge[cnt].to = b;
  25. edge[cnt].val = c;
  26. edge[cnt].next = head[a];
  27. head[a] = cnt ++;
  28.  
  29. edge[cnt].to = a;
  30. edge[cnt].val = ;//反向边容量初始化为0
  31. edge[cnt].next = head[b];
  32. head[b] = cnt ++;
  33. }
  34.  
  35. int bfs(int st, int ed)
  36. {
  37. mem(pos, -);
  38. while(!Q.empty())
  39. Q.pop();
  40. for(int i = ; i <= n; i ++)
  41. pre[i] = -;
  42. flow[st] = inf;
  43. Q.push(st);
  44. while(!Q.empty())
  45. {
  46. int a = Q.front();
  47. Q.pop();
  48. if(a == ed)
  49. return flow[ed];
  50. for(int i = head[a]; i != -; i = edge[i].next)
  51. {
  52. int to = edge[i].to;
  53. if(pre[to] == - && edge[i].val > && to != st)
  54. {
  55. flow[to] = min(flow[a], edge[i].val);
  56. pre[to] = a;
  57. pos[to] = i;//储存寻找到的路径各边的位置, 用于更新val时参与 ^ 运算
  58. Q.push(to);
  59. }
  60. }
  61. }
  62. return -;
  63. }
  64.  
  65. int max_flow(int st, int ed)
  66. {
  67. int inc;
  68. int ans = ;
  69. while((inc = bfs(st, ed)) != -)
  70. {
  71. int k = ed;
  72. while(k != st)
  73. {
  74. edge[pos[k]].val -= inc;//巧用 ^1 运算, 0 ^ 1 = 1, 1 ^1 = 0, 2 ^ 1 = 3, 3 ^ 1 = 2, 4 ^ 1 = 5, 5 ^ 1 = 4.
  75. // 所以这里链式前向星必须从0开始存边, 这样的话刚好正反向边与 ^ 运算一一对应,例如找到2边, 那么更新2, 3边, 找到3边,那么更新2, 3边
  76. edge[pos[k] ^ ].val += inc;
  77. k = pre[k];
  78. }
  79. ans += inc;
  80. }
  81. return ans;
  82. }
  83.  
  84. int main()
  85. {
  86. while(scanf("%d%d", &m, &n)!=EOF)
  87. {
  88. cnt = ;
  89. mem(head, -);
  90. for(int i = ; i <= m; i ++)
  91. {
  92. int a, b, c;
  93. scanf("%d%d%d", &a, &b, &c);
  94. if(a == b)
  95. continue;
  96. add(a, b, c);//链式前向星存储的是边的位置, 不需要特别处理重边, 存了的边都会在寻找增广路中被用到
  97. }
  98. int ans = max_flow(, n);
  99. printf("%d\n", ans);
  100. }
  101. return ;
  102. }

 2.Ford-Fulkerson

这个算法不常用, 可以用于理解后面的dinic算法, 不是很重要,在同学博客里复制过来,嘿嘿嘿

邻接矩阵

  1. #include<iostream>
  2. #include<string.h>
  3. #include<queue>
  4. using namespace std;
  5.  
  6. int map[][];
  7. int n,m;
  8. bool vis[];//标记该点有没有用过
  9.  
  10. int dfs(int start,int ed,int cnt)
  11. { //cnt是查找到的增广路中流量最小的边
  12. if(start == ed)
  13. return cnt; //起点等于终点,即已经查到一条可行增广路
  14. for(int i = ; i <= m; i ++)
  15. { //以起点start遍历与它相连的每一条边
  16. if(map[start][i] > && !vis[i])
  17. { //这条边是否可行
  18. vis[i] = true; //标记已经走过
  19. int flow = dfs(i, ed, min(cnt, map[start][i]));//递归查找
  20. if(flow > )
  21. { //回溯时更行map,这和EK的迭代更行差不多
  22. map[start][i] -= flow;
  23. map[i][start] += flow;
  24. return flow;
  25. }
  26. }
  27. }
  28. return ;//没找到
  29. }
  30. int Max_flow(int start, int ed)
  31. {
  32. int ans = ;
  33. while(true)
  34. {
  35. memset(vis, false, sizeof(vis));
  36. int inc = dfs(start, ed, 0x3f3f3f3f);//查找增广路
  37. if(inc == )
  38. return ans;//没有增广路了
  39. ans+=inc;
  40. }
  41. }
  42. int main()
  43. {
  44. int start, ed, w;
  45. while(cin >> n >> m)
  46. {
  47. memset(map, , sizeof(map));
  48. for(int i = ; i < n; i ++)
  49. {
  50. cin >> start >> ed >> w;
  51. if(start == ed)
  52. continue;
  53. map[start][ed] += w;
  54. }
  55. cout<<Max_flow(,m)<<endl;
  56. }
  57. return ;
  58. }

邻接表:

  1. 不写了, 这个算法不重要

3.dinic

这个算法是要求掌握。这是求解网络流较高效速度比较快的方法。

算法思想:

在寻找增广路之前进行bfs对边进行分层, 例如有边, 1->2,1->3,2->4, 3->4,2->3.那么分层之后就是第一层为点1,第二层为点2, 3.第三层为点4。然后在寻找增广路径时通过层次访问, 就避免了2->3这条边的访问。提高了效率。

邻接矩阵:

  1. #include<stdio.h>
  2. #include<queue>
  3. #include<string.h>
  4. #include<algorithm>
  5. #define mem(a, b) memset(a, b, sizeof(a))
  6. const int inf = 0x3f3f3f3f;
  7. using namespace std;
  8.  
  9. int m, n;
  10. int map[][];
  11. int dep[];//点所属的层次
  12. queue<int>Q;
  13.  
  14. int bfs(int st, int ed)
  15. {
  16. if(st == ed)
  17. return ;
  18. while(!Q.empty())
  19. Q.pop();
  20. mem(dep, -); //层次初始化
  21. dep[st] = ; //起点定义为第一层
  22. Q.push(st);
  23. while(!Q.empty())
  24. {
  25. int index = Q.front();
  26. Q.pop();
  27. for(int i = ; i <= n; i ++)
  28. {
  29. if(map[index][i] > && dep[i] == -)
  30. {
  31. dep[i] = dep[index] + ;
  32. Q.push(i);
  33. }
  34. }
  35. }
  36. return dep[ed] != -;//返回是否能成功分层,若无法分层说明找不到增广路径了,
  37. }
  38.  
  39. int dfs(int now, int ed, int cnt)
  40. {
  41. if(now == ed)//跳出条件, 找到了汇点,获得一条增广路径
  42. return cnt;
  43. for(int i = ; i <= n; i ++)
  44. {
  45. if(dep[i] == dep[now] + && map[now][i] > )
  46. {
  47. int flow = dfs(i, ed, min(cnt, map[now][i]));
  48. if(flow > )//这条增广路径上最小的边的flow值来更新整个路径
  49. {
  50. map[now][i] -= flow;
  51. map[i][now] += flow;
  52. return flow;
  53. }
  54. }
  55. }
  56. return -;//该种分层已经无法找到增广路径
  57. }
  58.  
  59. int max_flow(int st, int ed)
  60. {
  61. int ans = ;
  62. while(bfs(st, ed))
  63. {
  64. while()
  65. {
  66. int inc = dfs(st, ed, inf);
  67. if(inc == -)
  68. break;
  69. ans += inc;
  70. }
  71. }
  72. return ans;
  73. }
  74.  
  75. int main()
  76. {
  77. while(scanf("%d%d", &m, &n)!=EOF)
  78. {
  79. mem(map, );
  80. for(int i = ; i <= m; i ++)
  81. {
  82. int a, b, c;
  83. scanf("%d%d%d", &a, &b, &c);
  84. if(a == b)
  85. continue;
  86. map[a][b] += c;
  87. }
  88. printf("%d\n", max_flow(, n));
  89. }
  90. return ;
  91. }

邻接表(链式前向星实现):

  1. #include<stdio.h>
  2. #include<queue>
  3. #include<string.h>
  4. #include<algorithm>
  5. #define mem(a, b) memset(a, b, sizeof(a))
  6. const int inf = 0x3f3f3f3f;
  7. using namespace std;
  8.  
  9. int m, n;
  10. int head[], cnt;
  11. int dep[];
  12. queue<int>Q;
  13.  
  14. struct Edge
  15. {
  16. int to, next, val;
  17. }edge[];
  18.  
  19. void add(int a, int b, int c)// ^ 运算, 从0开始存边
  20. {
  21. edge[cnt].to = b;
  22. edge[cnt].val = c;
  23. edge[cnt].next = head[a];
  24. head[a] = cnt ++;
  25.  
  26. edge[cnt].to = a;
  27. edge[cnt].val = ;//反向边容量初始化 0
  28. edge[cnt].next = head[b];
  29. head[b] = cnt ++;
  30. }
  31.  
  32. int bfs(int st, int ed)
  33. {
  34. if(st == ed)
  35. return ;
  36. while(!Q.empty())
  37. Q.pop();
  38. mem(dep, -);//层次初始化
  39. dep[st] = ; //第一层定义为 1
  40. Q.push(st);
  41. while(!Q.empty())
  42. {
  43. int index = Q.front();
  44. Q.pop();
  45. for(int i = head[index]; i != -; i = edge[i].next)
  46. {
  47. int to = edge[i].to;
  48. if(edge[i].val > && dep[to] == -)
  49. {
  50. dep[to] = dep[index] + ;
  51. Q.push(to);
  52. }
  53. }
  54. }
  55. return dep[ed] != -;
  56. }
  57.  
  58. int dfs(int now, int ed, int cnt)
  59. {
  60. if(now == ed)
  61. return cnt;
  62. for(int i = head[now]; i != -; i = edge[i].next)
  63. {
  64. int to = edge[i].to;
  65. if(dep[to] == dep[now] + && edge[i].val > )
  66. {
  67. int flow = dfs(to, ed, min(cnt, edge[i].val));
  68. if(flow > )
  69. {
  70. edge[i].val -= flow;
  71. edge[i ^ ].val += flow;
  72. return flow;
  73. }
  74. }
  75. }
  76. return -;
  77. }
  78.  
  79. int max_flow(int st, int ed)
  80. {
  81. int ans = ;
  82. while(bfs(st, ed))
  83. {
  84. while()
  85. {
  86. int inc = dfs(st, ed, inf);
  87. if(inc == -)
  88. break;
  89. ans += inc;
  90. }
  91. }
  92. return ans;
  93. }
  94.  
  95. int main()
  96. {
  97. while(scanf("%d%d", &m, &n)!=EOF)
  98. {
  99. cnt = ;
  100. mem(head, -);
  101. for(int i = ; i <= m; i ++)
  102. {
  103. int a, b, c;
  104. scanf("%d%d%d", &a, &b, &c);
  105. if(a == b)
  106. continue;
  107. add(a, b, c);
  108. }
  109. printf("%d\n", max_flow(, n));
  110. }
  111. return ;
  112. }

  

网络流三大算法【邻接矩阵+邻接表】POJ1273的更多相关文章

  1. 数据结构学习笔记05图 (邻接矩阵 邻接表-->BFS DFS、最短路径)

    数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边& ...

  2. hdu 1874 畅通工程(spfa 邻接矩阵 邻接表)

    题目链接 畅通工程,可以用dijkstra算法实现. 听说spfa很好用,来水一发 邻接矩阵实现: #include <stdio.h> #include <algorithm> ...

  3. 第6章 图的学习总结(邻接矩阵&邻接表)

    我觉得图这一章的学习内容更有难度,其实图可以说是树结构更为普通的表现形式,它的每个元素都可以与多个元素之间相关联,所以结构比树更复杂,然而越复杂的数据结构在现实中用途就越大了,功能与用途密切联系,所以 ...

  4. POJ 3013 SPFA算法,邻接表的使用

    Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 19029   Accepted: 4 ...

  5. 图的全部实现(邻接矩阵 邻接表 BFS DFS 最小生成树 最短路径等)

    1 /** 2 * C: Dijkstra算法获取最短路径(邻接矩阵) 3 * 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> ...

  6. HDU 1874 畅通工程续(最短路/spfa Dijkstra 邻接矩阵+邻接表)

    题目链接: 传送门 畅通工程续 Time Limit: 1000MS     Memory Limit: 65536K Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路. ...

  7. <图论入门>邻接矩阵+邻接表

    非本人允许请勿转载. 趁热打铁,学会了邻接表把这个总结一下,以及感谢大佬uncle-lu!!!(奶一波)祝早日进队! 首先,图论入门就得是非常基础的东西,先考虑怎么把这个图读进去. 给定一个无向图,如 ...

  8. 最短路径SPFA算法(邻接表存法)

    queue <int> Q; void SPFA (int s) { int i, v; for(int i=0; i<=n; i++) dist[i]=INF; //初始化每点i到 ...

  9. c语言实现迪杰斯特拉算法(邻接表)

    储存结构,结构体的定义:(权值w用于表示两点间路径的花费) typedef int Status; typedef struct ENode//图的邻接表定义 { int adjVex;//任意顶点u ...

随机推荐

  1. learning memchr func

    extern void *memchr(const void *buf, int ch, size_t count);   用法:#include <string.h> 功能:从buf所指 ...

  2. linux终端相关概念解释及描述

    基本概念: 1. tty(终端设备的统称): tty一词源于Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西被 ...

  3. elastic search&logstash&kibana 学习历程(三)Logstash使用场景和安装部署

    Logstash基本介绍和使用场景 自我认为:logstash就是日志的采集收集,日志的搬运工,实时去采集日志.读取不同的数据源,并进行过滤,开发者自定义规范输出到目的地.日志的来源很多,如系统日志, ...

  4. c++继承子类构造函数问题

    c++中子类在继承基类的时候需要在构造函数中初始化变量.如果基类没有构造函数或者只有不带参数的构造函数,那么子类中的构造函数就不需要调用基类的构造函数了. 个人总结了两点子类中构造函数必须调用父类的构 ...

  5. 1 - ImageNet Classification with Deep Convolutional Neural Network (阅读翻译)

    ImageNet Classification with Deep Convolutional Neural Network 利用深度卷积神经网络进行ImageNet分类 Abstract We tr ...

  6. Leetcode题目39.组合总和(回溯+剪枝-中等)

    题目描述: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无 ...

  7. koa 实现下载文件

    文件下载需要使用到koa-send这个插件,该插件是一个静态文件服务的中间件,它可以用来实现文件下载的功能. 1.下载页面 static/download.html <!DOCTYPE html ...

  8. linux不能ping通主机,主机能ping通linux

    1).打开控制面板,点击“系统和安全”选项.然后打开“Windows防火墙”.2).点击进入“高级设置”,选择“入站规则”.3).在入门规则中找到“文件和打印机共享(回显请求-ICMPv4-In)”选 ...

  9. PHP将多个文件中的内容合并为新的文件

    function test() { $hostdir= iconv("utf-8","gbk","C:\Users\原万里\Desktop\日常笔记& ...

  10. mongodb 安装配置及简单使用

    步骤一: 下载网址:https://www.mongodb.com/download-center/community 根据自己的环境下载 步骤二: 安装过程只需要默认即可,需要注意的是连接工具“mo ...