POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

时间:2023-08-09 22:21:45
POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

一般预流推进算法:

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

算法思想:

对容量网络G 的一个预流f,如果存在活跃顶点,则说明该预流不是可行流。

预流推进算法就是要选择活跃顶点,并通过它把一定的流量推进到它的邻接顶点,尽可能将正的赢余减少为0。

由于算法最终目的是尽可能将流量推进到汇点Vt,因此算法总是首先寻求将流量推进到距离汇点Vt 最近的邻接顶点中。

由于每个顶点的距离标号可以表示顶点到汇点Vt 的距离,因此算法总是将流量沿着允许弧推进。

如果从当前活跃顶点出发没有允许弧,则增加该顶点的距离标号,使得从当前活跃顶点出发至少有一条允许弧。

算法实现基本框架:

(1) (预处理)取零流作为初始可行流,即f = { 0 },对源点Vs 发出的每条<Vs, u>,令f(Vs, u)= c(Vs, u);对任意的顶点v∈V,计算精确的距离标号d(v);令d(Vs) = n;

(2) 如果残留网络G'(V', E')中不存在活跃顶点,则算法结束,已经求得最大流,否则进入第(3)步;

(3) 在残留网络中选取活跃顶点u;如果存在顶点u 的某条出弧<u, v>为允许弧,则将min{ e(u),c'(u, v) }流量的流从顶点u 推进到顶点v;

否则令d(u) = min{ d(v)+1 | <u, v>∈E',且c'(u, v)>0}。转第(2)步。c'(u, v)为残留网络中弧<u, v>的容量。

名词解释:

赢余(excess):设u 是容量网络G(V, E)中的顶点,定义顶点u 的赢余为流入顶点u 的流量之和减去从顶点u 流出的流量之和,记为e(u)。

活跃顶点(active vertex):容量网络G 中,e(u) > 0 的顶点u(u≠Vs、Vt)称为活跃顶点。

预流(preflow):设f = { f(u, v) }是容量网络G(V, E)上的一个网络流,如果G 的每一条弧<u, v>都满足:0 ≤ f(u, v) ≤ c(u, v),<u, v>∈E。

另外,除源点Vs、汇点Vt 外每个顶点u的赢余e(u)都满足:e(u) ≥ 0,u≠Vs、Vt,则称该网络流f 为G 的预流。

算法实现:

[cpp] view
plain
 copy

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

  1. #include <bits/stdc++.h>//poj不支持超级头文件
  2. using namespace std;
  3. #define INF 0x3f3f3f3f
  4. const int maxn = 222;
  5. struct Push_Relablel//封装一般预流推进算法的结构体,时间复杂度O(n*n*m);
  6. {
  7. int res[maxn][maxn];//残留网络
  8. int dist[maxn];//距离函数
  9. int e[maxn];//赢余
  10. int n;//结点数
  11. int max_flow;//最大流
  12. void init(int x)
  13. {
  14. n = x;
  15. memset(res, 0, sizeof(res));
  16. memset(dist, 0, sizeof(dist));
  17. memset(e, 0, sizeof(e));
  18. }
  19. void add(int u, int v, int c)
  20. {
  21. res[u][v] += c;
  22. }
  23. int push_relablel(int s, int t)
  24. {
  25. max_flow = 0;
  26. queue<int> Q;
  27. Q.push(s);
  28. e[s] = INF; e[t] = INF;
  29. dist[s] = n;
  30. while(!Q.empty())
  31. {
  32. int u = Q.front(); Q.pop();
  33. for(int i = 1; i <= n; ++i)
  34. {
  35. int p = res[u][i] < e[u] ? res[u][i] : e[u];
  36. if(p>0 && (u == s || dist[u] == dist[i] + 1))
  37. {
  38. res[u][i] -=p;
  39. res[i][u] +=p;
  40. if(i == t) max_flow += p;
  41. e[u] -= p;
  42. e[i] += p;
  43. if(i!=s && i!=t) Q.push(i);
  44. }
  45. }
  46. if(u!=s && u!=t && e[u] > 0)
  47. {
  48. dist[u]++;
  49. Q.push(u);
  50. }
  51. }
  52. return max_flow;
  53. }
  54. } G;
  55. int main()
  56. {
  57. int m, n, u, v, c;
  58. while(~scanf("%d%d", &m, &n))
  59. {
  60. G.init(n);
  61. while(m--)
  62. {
  63. scanf("%d%d%d", &u, &v, &c);
  64. G.add(u, v, c);
  65. }
  66. cout<<G.push_relablel(1, n)<<endl;
  67. }
  68. return 0;
  69. }

最高标号预流推进算法:

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

算法思想:

一般预流推进算法的缺陷在于非饱和推进。

从具有最大距离标号的活跃节点开始预流推进,使得距离标号较小的活跃顶点累积尽可能多的来自距离标号较大的活跃顶点的流量,然后对累积的盈余进行推进,可能会减少非饱和推进的次数。

用优先队列优化即可。

算法实现:

[cpp] view
plain
 copy
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define INF 0x3f3f3f3f
  4. const int maxn = 222;
  5. struct List
  6. {
  7. int x, dist;
  8. List(int x_, int dist_): x(x_), dist(dist_){}
  9. bool friend operator < (const List& a, const List& b){
  10. return a.dist < b.dist;
  11. }
  12. };
  13. struct Push_Relablel         //最高标号预流推进算法,时间复杂度O(n * n * sqrt(m))
  14. {
  15. int res[maxn][maxn];
  16. int dist[maxn];
  17. int e[maxn];
  18. int n;
  19. int max_flow;
  20. void init(int x)
  21. {
  22. n = x;
  23. memset(res, 0, sizeof(res));
  24. memset(dist, 0, sizeof(dist));
  25. memset(e, 0, sizeof(e));
  26. }
  27. void add(int u, int v, int c)
  28. {
  29. res[u][v] += c;
  30. }
  31. int push_relablel(int s, int t)
  32. {
  33. max_flow = 0;
  34. dist[s] = n;
  35. e[s] = INF; e[t] = INF;
  36. priority_queue<List> Q;
  37. Q.push(List(s, dist[s]));
  38. while(!Q.empty())
  39. {
  40. List q = Q.top(); Q.pop();
  41. int u = q.x;
  42. for(int i = 1; i <= n; ++i)
  43. {
  44. int p = res[u][i] < e[u] ? res[u][i] : e[u];
  45. if(p>0 && (u==s || dist[u] == dist[i] + 1))
  46. {
  47. res[u][i] -= p; res[i][u] += p;
  48. if(i == t) max_flow += p;
  49. e[u] -= p; e[i] += p;
  50. if(i != s && i != t) Q.push(List(i, dist[i]));
  51. }
  52. }
  53. if(u!=s && u!=t && e[u]>0)
  54. {
  55. dist[u]++;
  56. Q.push(List(u, dist[u]));
  57. }
  58. }
  59. return max_flow;
  60. }
  61. } G;
  62. int main()
  63. {
  64. int m, n, u, v, c;
  65. while(~scanf("%d%d", &m, &n))
  66. {
  67. G.init(n);
  68. while(m--)
  69. {
  70. scanf("%d%d%d", &u, &v, &c);
  71. G.add(u, v, c);
  72. }
  73. cout<<G.push_relablel(1, n)<<endl;
  74. }
  75. return 0;
  76. }

最短增广路算法(SAP):

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

算法思想:

每次在层次网络中找一个含弧最少的增广路进行增广;

算法实现步骤:

(1) 初始化容量网络和网络流;

(2) 构造残留网络和层次网络,若汇点不在层次网络中,则算法结束;

(3) 在层次网络中不断用BFS 增广,直到层次网络中没有增广路为止;每次增广完毕,在层次网络中要去掉因改进流量而导致饱和的弧;

(4) 转步骤(2)。

算法实现:

[cpp] view
plain
 copy
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <queue>
  4. #include <iostream>
  5. using namespace std;
  6. #define INF 0x3f3f3f3f
  7. const int maxn = 222;
  8. struct EK              //Edmonds-Krap 算法,又称SAP算法,时间复杂度O(n*m*m);
  9. {
  10. int res[maxn][maxn];
  11. int pre[maxn];
  12. int n;
  13. void init(int x)
  14. {
  15. n = x;
  16. memset(res, 0, sizeof(res));
  17. }
  18. void add(int u, int v, int c)
  19. {
  20. res[u][v] += c;
  21. }
  22. bool bfs(int s, int t)
  23. {
  24. queue<int> Q;
  25. memset(pre, -1, sizeof(pre));
  26. Q.push(s);
  27. while(!Q.empty())
  28. {
  29. int u = Q.front(); Q.pop();
  30. for(int i = 1; i <= n; ++i)
  31. {
  32. if(res[u][i] && pre[i] == -1)         //有增广量而没有加入增广路
  33. {
  34. pre[i] = u;
  35. if(i == t) return true;
  36. Q.push(i);
  37. }
  38. }
  39. }
  40. return false;
  41. }
  42. int sap(int s, int t)
  43. {
  44. int max_flow = 0;
  45. while(bfs(s, t))
  46. {
  47. int a = INF;
  48. for(int i = t; i != s; i = pre[i])
  49. a = min(a, res[pre[i]][i]);
  50. for(int i = t; i != s; i = pre[i])
  51. {
  52. res[pre[i]][i] -= a;
  53. res[i][pre[i]] += a;
  54. }
  55. max_flow += a;
  56. }
  57. return max_flow;
  58. }
  59. } G;
  60. int main()
  61. {
  62. int n, m, u, v, c;
  63. while(~scanf("%d%d", &m, &n))
  64. {
  65. G.init(n);
  66. while(m--)
  67. {
  68. scanf("%d%d%d", &u, &v, &c);
  69. G.add(u, v, c);
  70. }
  71. cout<< G.sap(1, n) <<endl;
  72. }
  73. return 0;
  74. }

连续最短增广路算法(Dinic):

POJ1273 网络流-->最大流-->模板级别-->最大流常用算法总结

算法思想:

Dinic 算法的思想也是分阶段地在层次网络中增广。它与最短增广路算法不同之处是:在Dinic 算法中,只需一次DFS 过程就可以实现多次增广。

算法实现步骤:

(1) 初始化容量网络和网络流;

(2) 构造残留网络和层次网络,若汇点不在层次网络中,则算法结束;

(3) 在层次网络中用一次DFS 过程进行增广,DFS 执行完毕,该阶段的增广也执行完毕;

(4) 转步骤(2)。

算法实现:

[cpp] view
plain
 copy
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define INF 0x3f3f3f3f
  4. const int maxn = 222;
  5. struct Dinic                //时间复杂度O(n*n*m)
  6. {
  7. int res[maxn][maxn];
  8. int dist[maxn];
  9. int n;
  10. void init(int x)
  11. {
  12. n = x;
  13. memset(res, 0, sizeof(res));
  14. }
  15. void add(int u, int v, int c)
  16. {
  17. res[u][v] += c;
  18. }
  19. int bfs(int s)
  20. {
  21. memset(dist, 0xff, sizeof(dist));
  22. dist[s] = 0;
  23. queue<int> Q;
  24. Q.push(s);
  25. while(!Q.empty())
  26. {
  27. int u = Q.front(); Q.pop();
  28. for(int i = 1; i <= n; ++i)
  29. {
  30. if(dist[i] < 0 && res[u][i] > 0)
  31. {
  32. dist[i] = dist[u] + 1;
  33. Q.push(i);
  34. }
  35. }
  36. }
  37. if(dist[n] > 0) return true;
  38. return false;
  39. }
  40. int Find(int x, int low)
  41. {
  42. int a = 0;
  43. if(x == n) return low;
  44. for(int i = 1; i <= n; ++i)
  45. {
  46. if(res[x][i] > 0 && dist[i] == dist[x] + 1 && (a = Find(i, min(low, res[x][i]))))
  47. {
  48. res[x][i] -= a;
  49. res[i][x] += a;
  50. return a;
  51. }
  52. }
  53. return 0;
  54. }
  55. int dinic(int s, int t)
  56. {
  57. int max_flow = 0, tmp;
  58. while(bfs(1))
  59. {
  60. while(tmp = Find(1, INF))
  61. max_flow += tmp;
  62. }
  63. return max_flow;
  64. }
  65. } G;
  66. int main()
  67. {
  68. int m, n, u, v, c;
  69. while(~scanf("%d%d", &m, &n))
  70. {
  71. G.init(n);
  72. while(m--)
  73. {
  74. scanf("%d%d%d", &u, &v, &c);
  75. G.add(u, v, c);
  76. }
  77. cout<< G.dinic(1, n) <<endl;
  78. }
  79. return 0;
  80. }