[关闭]
@rg070836rg 2015-12-28T12:35:36.000000Z 字数 4940 阅读 1413

算法概论实验十二

算法概论实验

  1. 实验十二
  2. 实验目的与要求:
  3. 1 理解与掌握求解最大流与最小割的基本算法。
  4. 2 学会应用最大流与最小割算法解决实际问题。
  5. 1.实现Ford-Fulkerson算法,求出给定图中从源点s到汇点t的最大流,并输出最小割。
  6. 2. 设计与实现二部图匹配(Bipartite Matching)问题的算法。

1.Ford-Fulkerson算法

  1. //void *memset(void *s,int c,size_t n)将已开辟内存空间 s 的首 n 个字节的值设为值 c
  2. //#include <Ford_Fulkerson>
  3. #include <algorithm>
  4. #include <cstdio>
  5. #include <list>
  6. #include <queue>
  7. #include <iostream>
  8. using namespace std;
  9. #define INFI 1000
  10. typedef struct _mark
  11. {
  12. int pre_suc;
  13. int max_incr;
  14. }MARK;
  15. int iteration = 0;//增光路径数目
  16. const int N = 100;
  17. list<int> setS;
  18. bool isMark[N], isCheck[N], isDone;
  19. MARK markList[N];
  20. int c[N][N], f[N][N];
  21. int n; //顶点数
  22. int Maxflow()
  23. {
  24. int flow = 0;
  25. for (int i = 0; i<n; i++)
  26. {
  27. flow += f[0][i];
  28. }
  29. return flow;
  30. }
  31. void Mincut()//isMark的点就是最小割
  32. {
  33. int i = 0;
  34. while (i<n)
  35. {
  36. if (isMark[i])
  37. setS.push_back(i);
  38. i++;
  39. }
  40. }
  41. int IncrFlowAuxi(int index)//计算增广路径中的最大可增量
  42. {
  43. if (index == 0)
  44. return markList[index].max_incr;
  45. int prev = markList[index].pre_suc;
  46. int maxIncr = markList[index].max_incr;
  47. return min(maxIncr, IncrFlowAuxi(prev));//递归求瓶颈值为最大增量
  48. }
  49. void IncrFlow()//增广路径的增加
  50. {
  51. iteration++;
  52. int incr = IncrFlowAuxi(n - 1); //最大可增量
  53. int index = n - 1;
  54. int prev;
  55. while (index != 0)
  56. {
  57. prev = markList[index].pre_suc;
  58. f[prev][index] += incr; //增广路径增加后,相应的流量进行更新
  59. index = prev;
  60. }
  61. }
  62. void Mark(int index, int pre_suc, int max_incr)//被标记表示可能被纳入新路径
  63. {
  64. isMark[index] = true;
  65. markList[index].pre_suc = pre_suc;//前驱
  66. markList[index].max_incr = max_incr;//当前路径的流值
  67. }
  68. void Check(int i)//被mark且被check的点表示已经被纳入新路径
  69. {
  70. isCheck[i] = true;
  71. for (int j = 0; j<n; j++)
  72. {
  73. if (c[i][j]>0 && !isMark[j] && c[i][j]>f[i][j])//forward 边
  74. Mark(j, i, min(markList[i].max_incr, c[i][j] - f[i][j]));
  75. if (c[j][i]>0 && !isMark[j] && f[j][i]>0)//reverse 边
  76. Mark(j, i, min(markList[i].max_incr, f[j][i]));
  77. }
  78. }
  79. //ford_fulkerson算法
  80. int ford_fulkerson()
  81. {
  82. int i;
  83. while (1)//一次循环找到一个新路径
  84. {
  85. isDone = true;
  86. i = 0;
  87. while (i<n)//一次循环判断上次循环是否有找到新路径,若无则表明没有新路径,终止算法
  88. {
  89. if (isMark[i] && !isCheck[i]) //判断是否所有标记的点都已被检查:若是,结束整个算法
  90. {
  91. isDone = false;
  92. break;
  93. }
  94. i++;
  95. }
  96. if (isDone) //算法结束,则计算最小割和最大流
  97. {
  98. Mincut();
  99. return Maxflow();
  100. }
  101. while (i<n)//贪心法构建新路径
  102. {
  103. if (isMark[i] && !isCheck[i]) {
  104. Check(i);
  105. i = 0;
  106. }
  107. if (isMark[n - 1]) //如果汇t被标记,说明找到了一条增广路径,则增加该条路径的最大可增加量
  108. {
  109. IncrFlow();
  110. memset(isMark + 1, false, n - 1); //增加该增广路径后,除了源s,其余标记抹去
  111. memset(isCheck, false, n);
  112. }
  113. else i++;
  114. }
  115. }
  116. }
  117. int main()
  118. {
  119. /*
  120. 测试值:ppt上的例子
  121. 0 20 10 0
  122. 0 0 30 10
  123. 0 0 0 20
  124. 0 0 0 0
  125. */
  126. n = 4;
  127. for (int k = 0; k < n; ++k)
  128. {
  129. memset(c[k], 0, sizeof(c[0][0])*n);
  130. memset(f[k], 0, sizeof(f[0][0])*n); //初始各分支流量为0
  131. memset(isMark, false, n);
  132. memset(isCheck, false, n);
  133. }
  134. isMark[0] = true; //给源做永久标记
  135. markList[0].max_incr = INFI;
  136. markList[0].pre_suc = INFI;
  137. c[0][1] = 20;
  138. c[0][2] = 10;
  139. c[1][2] = 30;
  140. c[1][3] = 10;
  141. c[2][3] = 20;
  142. cout << "最大流为:" << ford_fulkerson() << endl;
  143. cout << "最大割的S集合为:{";
  144. for (list<int>::iterator i = setS.begin(); i != setS.end(); i++)
  145. {
  146. printf("%d ", *i);
  147. }
  148. cout << "}" << endl;
  149. cout << "增广路径个数为:" << iteration << endl;
  150. system("PAUSE");
  151. }

2. 二部图匹配

  1. //void *memset(void *s,int c,size_t n)将已开辟内存空间 s 的首 n 个字节的值设为值 c
  2. //#include <Ford_Fulkerson>
  3. #include <algorithm>
  4. #include <cstdio>
  5. #include <list>
  6. #include <queue>
  7. #include <iostream>
  8. using namespace std;
  9. #define INFI 1000
  10. typedef struct _mark
  11. {
  12. int pre_suc;
  13. int max_incr;
  14. }MARK;
  15. int iteration = 0;//增光路径数目
  16. const int N = 100;
  17. list<int> setS;
  18. bool isMark[N], isCheck[N], isDone;
  19. MARK markList[N];
  20. int c[N][N], f[N][N];
  21. int n; //顶点数
  22. int Maxflow()
  23. {
  24. int flow = 0;
  25. for (int i = 0; i<n; i++)
  26. {
  27. flow += f[0][i];
  28. }
  29. return flow;
  30. }
  31. void Mincut()//isMark的点就是最小割
  32. {
  33. int i = 0;
  34. while (i<n)
  35. {
  36. if (isMark[i])
  37. setS.push_back(i);
  38. i++;
  39. }
  40. }
  41. int IncrFlowAuxi(int index)//计算增广路径中的最大可增量
  42. {
  43. if (index == 0)
  44. return markList[index].max_incr;
  45. int prev = markList[index].pre_suc;
  46. int maxIncr = markList[index].max_incr;
  47. return min(maxIncr, IncrFlowAuxi(prev));//递归求瓶颈值为最大增量
  48. }
  49. void IncrFlow()//增广路径的增加
  50. {
  51. iteration++;
  52. int incr = IncrFlowAuxi(n - 1); //最大可增量
  53. int index = n - 1;
  54. int prev;
  55. while (index != 0)
  56. {
  57. if (index != n - 1)
  58. cout << index << " ";
  59. prev = markList[index].pre_suc;
  60. f[prev][index] += incr; //增广路径增加后,相应的流量进行更新
  61. index = prev;
  62. }
  63. cout << endl;
  64. }
  65. void Mark(int index, int pre_suc, int max_incr)//被标记表示可能被纳入新路径
  66. {
  67. isMark[index] = true;
  68. markList[index].pre_suc = pre_suc;//前驱
  69. markList[index].max_incr = max_incr;//当前路径的流值
  70. }
  71. void Check(int i)//被mark且被check的点表示已经被纳入新路径
  72. {
  73. isCheck[i] = true;
  74. for (int j = 0; j<n; j++)
  75. {
  76. if (c[i][j]>0 && !isMark[j] && c[i][j]>f[i][j])//forward 边
  77. Mark(j, i, min(markList[i].max_incr, c[i][j] - f[i][j]));
  78. if (c[j][i]>0 && !isMark[j] && f[j][i]>0)//reverse 边
  79. Mark(j, i, min(markList[i].max_incr, f[j][i]));
  80. }
  81. }
  82. //ford_fulkerson算法
  83. int ford_fulkerson()
  84. {
  85. int i;
  86. while (1)//一次循环找到一个新路径
  87. {
  88. isDone = true;
  89. i = 0;
  90. while (i<n)//一次循环判断上次循环是否有找到新路径,若无则表明没有新路径,终止算法
  91. {
  92. if (isMark[i] && !isCheck[i]) //判断是否所有标记的点都已被检查:若是,结束整个算法
  93. {
  94. isDone = false;
  95. break;
  96. }
  97. i++;
  98. }
  99. if (isDone) //算法结束,则计算最小割和最大流
  100. {
  101. Mincut();
  102. return Maxflow();
  103. }
  104. while (i<n)//贪心法构建新路径
  105. {
  106. if (isMark[i] && !isCheck[i]) {
  107. Check(i);
  108. i = 0;
  109. }
  110. if (isMark[n - 1]) //如果汇t被标记,说明找到了一条增广路径,则增加该条路径的最大可增加量
  111. {
  112. IncrFlow();
  113. memset(isMark + 1, false, n - 1); //增加该增广路径后,除了源s,其余标记抹去
  114. memset(isCheck, false, n);
  115. }
  116. else i++;
  117. }
  118. }
  119. }
  120. int main()
  121. {
  122. //测试数据为ppt第40页的图,只实现了二部图的最大匹配
  123. n = 12;
  124. for (int k = 0; k < n; ++k)
  125. {
  126. memset(c[k], 0, sizeof(c[0][0])*n);
  127. memset(f[k], 0, sizeof(f[0][0])*n); //初始各分支流量为0
  128. memset(isMark, false, n);
  129. memset(isCheck, false, n);
  130. }
  131. isMark[0] = true; //给源做永久标记
  132. markList[0].max_incr = INFI;
  133. markList[0].pre_suc = INFI;
  134. c[1][6] = INFI;
  135. c[1][7] = INFI;
  136. c[2][7] = INFI;
  137. c[3][6] = INFI;
  138. c[3][8] = INFI;
  139. c[3][9] = INFI;
  140. c[4][7] = INFI;
  141. c[4][10] = INFI;
  142. c[5][7] = INFI;
  143. c[5][10] = INFI;
  144. for (int i = 1; i < n / 2; i++)
  145. c[0][i] = 1;
  146. for (int i = n/2; i < n-1; i++)
  147. c[i][n-1] = 1;
  148. cout << "最大匹配结果为:" << endl;
  149. int result= ford_fulkerson();
  150. cout << "匹配边个数为:" << result << endl;
  151. system("PAUSE");
  152. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注