当前位置:   article > 正文

【深圳大学算法设计与分析】实验六 最大流应用问题_深圳大学算法设计与分析实验六

深圳大学算法设计与分析实验六

实验目的

(1)掌握最大流算法思想。

(2)学会用最大流算法求解应用问题。

实验内容与结果

为了完成实验,需要先了解几个算法:

1Ford-Fulkerson算法(FF算法)

Ford-Fulkerson算法是一种用于解决最大流问题的经典算法。最大流问题是在一个有向图中找到从源节点到汇节点的最大流量路径。

算法的基本思想是不断寻找增广路径,并通过增加路径上的流量来增加总体的流量。增广路径是指从源节点到汇节点的路径,其上的边的剩余容量大于零。通过不断寻找增广路径并增加流量,直到无法找到增广路径为止,就可以得到最大流。

Ford-Fulkerson算法的基本步骤如下:

1. 初始化流量为0。

2. 在图中找到一条增广路径。可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来寻找增广路径。

3. 如果存在增广路径,则确定路径上的最小剩余容量,这将成为增加的流量。

4. 在增广路径上更新每条边的流量,将流量增加到正向边上,减少流量到反向边上。

5. 重复步骤2-4,直到没有增广路径可以找到。

6. 最终,流量的总和就是最大流。

Ford-Fulkerson算法本身不提供最优解的保证。为了得到最大流的最优解,可以使用不同的方法来选择增广路径,如Edmonds-Karp算法,它使用广度优先搜索来选择增广路径,并保证在每次寻找增广路径时使用最短路径。

2Edmonds-Karp算法(EK算法)

Edmonds-Karp算法是一种基于Ford-Fulkerson算法的改进版本,用于解决最大流问题。与Ford-Fulkerson算法不同的是,Edmonds-Karp算法使用广度优先搜索(BFS)来选择增广路径,从而保证每次找到的增广路径是最短路径。

Edmonds-Karp算法的基本步骤如下:

1. 初始化流量为0。

2. 在图中使用广度优先搜索(BFS)找到一条增广路径。BFS会按照节点的层级顺序搜索,确保找到的路径是最短路径。

3. 如果存在增广路径,则确定路径上的最小剩余容量,这将成为增加的流量。

4. 在增广路径上更新每条边的流量,将流量增加到正向边上,减少流量到反向边上。

5. 重复步骤2-4,直到没有增广路径可以找到。

6. 最终,流量的总和就是最大流。

通过使用广度优先搜索来选择最短增广路径,Edmonds-Karp算法保证了每次迭代增加的流量是最优的。算法的时间复杂度为O(V * E2),其中V是节点数,E是边数。

Edmonds-Karp算法的优点是可以得到最大流的最优解,而不仅仅是一个可行解。此外,由于使用了广度优先搜索,它的运行时间相对较短,适用于中等规模的问题。

综上所述,Edmonds-Karp算法是一种基于广度优先搜索的改进版Ford-Fulkerson算法,用于解决最大流问题,通过保证每次找到的增广路径是最短路径来获得最优解。

EK算法核心部分伪代码如下

3Dinic算法

Dinic算法是一种用于解决最大流问题的高效算法。它基于增广路径的概念,并使用了一种称为层次图的数据结构来加速查找增广路径的过程。

Dinic算法的基本步骤如下:

1. 构建初始的残余网络,其中所有边的初始流量为0。

2. 构建层次图,即通过广度优先搜索(BFS)确定每个节点的层级,从源节点开始,每一层只包含从前一层到达的节点。

3. 当存在层次图中的增广路径时,执行以下步骤:

   a. 在层次图中使用深度优先搜索(DFS)查找增广路径,路径上的边必须满足残余容量大于0。

   b. 在DFS过程中,尽量选择残余容量最大的边,以加快算法的收敛速度。

   c. 如果找到增广路径,则确定路径上的最小残余容量,这将成为增加的流量。

   d. 在增广路径上更新每条边的流量,增加流量到正向边上,减少流量到反向边上。

   e. 更新残余网络,即更新每条边的残余容量。

   f. 重复步骤3直到没有增广路径可以找到。

4. 最终,流量的总和就是最大流。

Dinic算法的主要优势在于它的高效性。在每次迭代中,Dinic算法可以找到多条增广路径,并且在每次迭代中,层次图的层级结构都会保持不变,因此可以避免重复计算。算法的时间复杂度为O(V2 * E),其中V是节点数,E是边数,这使得Dinic算法在大多数实际情况下具有较好的性能。

Danic算法核心部分伪代码如下

Danic算法相较EK算法的优势

1. 时间复杂度更优:Dinic算法的时间复杂度为O(V2 * E),而Edmonds-Karp算法的时间复杂度为O(V * E2)。在稠密图(边数接近顶点数的平方)中,Dinic算法通常比Edmonds-Karp算法更快。这是因为Dinic算法通过使用层次图和BFS构造增广路径,减少了不必要的搜索和迭代次数。

2. 增广路径选择更优:Dinic算法在每次迭代中选择残余容量最大的边作为增广路径的一部分,从而在每次迭代中增加更多的流量。这使得Dinic算法收敛更快,可以找到更快的最大流。

3. 不会出现返流:Edmonds-Karp算法在每次迭代时使用BFS选择增广路径,可能会导致在残余图中出现返流边,从而需要对返流边进行反复的搜索和调整。而Dinic算法使用层次图构建增广路径,保证了每次迭代中不会出现返流边,从而减少了不必要的操作。

4. 空间效率更高:Dinic算法使用层次图作为辅助数据结构,而不需要维护整个残余图。这使得Dinic算法的空间复杂度较低,只需O(V)的额外空间。

综上所述,Dinic算法相比于Edmonds-Karp算法在时间复杂度、增广路径选择、返流处理和空间效率等方面具有优势。它能够更快地找到最大流,并且在大多数实际情况下表现更优。

因此,接下来使用Danic算法完成实验。

简化题目:

一个医院有 n 名医生,现有 k 个公共假期,每一个公共假期有q天。例如,“五一”假期由5月1日至5月7日一共7天。“元旦”假期由1月1日至1月3日一共3天。

每名医生均有一个可值班的假日集合。例如,李医生可以值班的假日集合包括“五一”假期中的5月3日、5月4日,和“元旦”假期中的1月2日。

每个医生总共最多值班c天。

每个医生在一个假期中只能值班1天。例如,安排李医生在某个假期的某天值班后,那么便不能安排李医生在该假期的其它天值班。

根据上述场景完成下面任务:

(1)生成数据有n名医生,k个假期,每个假期有q天,每个医生总共最多值班c天,每个医生在一个假期中只能值班1天。

(2)用Danic算法求解上述问题,基于生成的数据,设计一个流网络;并计算出排班的方案。

可以设计如下图所示的流网络,源点连接医生,每个医生再连接可值班的假期,假期再连接可值班的假日,最后找到的有流量的的边即为分配方案。

步骤如下:

1. 创建节点:

   - 源点(Source):表示医生的起始节点。

   - 医生节点:表示每个医生,例如医生n1、n2、n3。

   - 假期节点:表示每个假期,例如假期k1、k2。

   - 假日节点:表示每个假期的每一天,例如假期k1的第一天、第二天、第三天,假期k2的第一天、第二天、第三天。

   - 汇点(Sink):表示流网络的终点。

2. 添加边和容量:

   - 源点到医生节点:将容量设置为每个医生可值班的最大天数c。

   - 医生节点到假期节点:将容量设置为1,表示医生只能在一个假期中值班一天。

   - 假期节点到假日节点:将容量设置为1,表示每个假期的每一天只能安排一个医生值班。

   - 假日节点到汇点:将容量设置为1,表示每一天只能安排一个医生值班。

3. 运行Dinic算法:

   - 使用Dinic算法计算最大流量,从源点流向汇点的最大流量即表示可安排的最大值班天数。

   - 根据最大流量的结果,确定每个医生在哪些假期的哪些假日值班。

运行后结果如下

数据统计如下

绘折线图如下

可以看出随着数据规模增大,耗时也会逐渐增大。

但因为采用了Danic算法优化,因此数据规模增大后,耗时仍在在ms级内,属于可以接受的范围。

实验总结

通过本次实验,加深了对最大流算法的理解和运用,加深了流网络、割、增广路径、残留网络、残留容量等概念的理解。理解了Ford-Fulkerson算法、EDMONDS-KARP算法、Dinic算法。

实验伪代码

  1. //EK算法
  2. function EdmondsKarpAlgorithm(graph, source, sink):
  3. // 初始化残余图,将所有边的流量设为0
  4. residualGraph = InitializeResidualGraph(graph)
  5. // 初始化最大流量为0
  6. maxFlow = 0
  7. // 在残余图中存在增广路径时继续循环
  8. while ThereExistsAugmentingPath(residualGraph, source, sink):
  9. // 使用广度优先搜索(BFS)找到一条增广路径
  10. augmentingPath = FindAugmentingPath(residualGraph, source, sink)
  11. // 找到增广路径后,计算路径上的最小残余容量
  12. minCapacity = FindMinCapacity(augmentingPath)
  13. // 在增广路径上更新每条边的流量
  14. UpdateFlow(residualGraph, augmentingPath, minCapacity)
  15. // 更新最大流量
  16. maxFlow = maxFlow + minCapacity
  17. // 返回最大流量
  18. return maxFlow
  19. function InitializeResidualGraph(graph):
  20. residualGraph = createEmptyGraph()
  21. // 在残余图中复制原始图的边,并将初始流量设为0
  22. for each edge (u, v) in graph:
  23. residualGraph.addEdge(u, v, capacity=graph[u][v], flow=0)
  24. return residualGraph
  25. function ThereExistsAugmentingPath(residualGraph, source, sink):
  26. // 使用广度优先搜索(BFS)查找从源节点到汇节点的增广路径
  27. visited = createEmptySet()
  28. queue = createEmptyQueue()
  29. queue.enqueue(source)
  30. visited.add(source)
  31. while queue is not empty:
  32. currentVertex = queue.dequeue()
  33. // 遍历当前节点的邻居节点
  34. for each neighbor in residualGraph.getNeighbors(currentVertex):
  35. // 如果邻居节点未被访问且残余容量大于0,则加入队列并标记为已访问
  36. if neighbor not in visited and residualGraph.getResidualCapacity(currentVertex, neighbor) > 0:
  37. queue.enqueue(neighbor)
  38. visited.add(neighbor)
  39. // 记录从源节点到当前节点的路径
  40. residualGraph.setParent(neighbor, currentVertex)
  41. // 如果汇节点被访问到了,说明存在增广路径
  42. return sink in visited
  43. function FindAugmentingPath(residualGraph, source, sink):
  44. // 使用深度优先搜索(DFS)找到从源节点到汇节点的增广路径
  45. path = []
  46. currentVertex = sink
  47. while currentVertex != source:
  48. // 将当前节点添加到路径中
  49. path.append(currentVertex)
  50. // 获取当前节点的父节点
  51. parent = residualGraph.getParent(currentVertex)
  52. // 在残余图中删除当前节点和父节点之间的边,相当于将其标记为已访问
  53. residualGraph.removeEdge(parent, currentVertex)
  54. currentVertex = parent
  55. // 将源节点添加到路径中
  56. path.append(source)
  57. // 反转路径,使其从源节点到汇节点
  58. path.reverse()
  59. // 返回增广路径
  60. return path
  61. function FindMinCapacity(augmentingPath):
  62. // 初始化最小残余容量为正无穷
  63. minCapacity = infinity
  64. // 遍历增广路径上的边,找到最小的残余容量
  65. for each edge (u, v) in augmentingPath:
  66. // 获取当前边的残余容量
  67. residualCapacity = residualGraph.getResidualCapacity(u, v)
  68. // 更新最小残余容量
  69. minCapacity = min(minCapacity, residualCapacity)
  70. // 返回最小残余容量
  71. return minCapacity
  72. function UpdateFlow(residualGraph, augmentingPath, minCapacity):
  73. // 在增广路径上更新每条边的流量
  74. for each edge (u, v) in augmentingPath:
  75. // 增加流量到正向边上
  76. residualGraph.increaseFlow(u, v, minCapacity)
  77. // 减少流量到反向边上
  78. residualGraph.decreaseFlow(v, u, minCapacity)
  79. //Danic算法
  80. function DinicAlgorithm(graph, source, sink):
  81. // 构建初始的残余网络,将所有边的初始流量设为0
  82. residualGraph = InitializeResidualGraph(graph)
  83. // 初始化最大流量为0
  84. maxFlow = 0
  85. // 构建层次图(Layered Graph),即确定每个节点的层级
  86. while ConstructLayeredGraph(residualGraph, source, sink):
  87. // 在层次图中存在增广路径时继续循环
  88. while true:
  89. // 使用深度优先搜索(DFS)查找增广路径
  90. augmentingPath = FindAugmentingPath(residualGraph, source, sink)
  91. // 如果没有找到增广路径,跳出内层循环
  92. if augmentingPath is None:
  93. break
  94. // 计算路径上的最小残余容量
  95. minCapacity = FindMinCapacity(augmentingPath)
  96. // 在增广路径上更新每条边的流量
  97. UpdateFlow(residualGraph, augmentingPath, minCapacity)
  98. // 更新最大流量
  99. maxFlow = maxFlow + minCapacity
  100. // 返回最大流量
  101. return maxFlow
  102. function InitializeResidualGraph(graph):
  103. residualGraph = createEmptyGraph()
  104. // 在残余图中复制原始图的边,并将初始流量设为0
  105. for each edge (u, v) in graph:
  106. residualGraph.addEdge(u, v, capacity=graph[u][v], flow=0)
  107. return residualGraph
  108. function ConstructLayeredGraph(residualGraph, source, sink):
  109. // 使用广度优先搜索(BFS)构建层次图
  110. residualGraph.resetLevels()
  111. residualGraph.setLevel(source, 0)
  112. queue = createEmptyQueue()
  113. queue.enqueue(source)
  114. while queue is not empty:
  115. currentVertex = queue.dequeue()
  116. // 遍历当前节点的邻居节点
  117. for each neighbor in residualGraph.getNeighbors(currentVertex):
  118. // 如果邻居节点未被标记层级且残余容量大于0,则将其标记为下一层级
  119. if residualGraph.getLevel(neighbor) == -1 and residualGraph.getResidualCapacity(currentVertex, neighbor) > 0:
  120. residualGraph.setLevel(neighbor, residualGraph.getLevel(currentVertex) + 1)
  121. queue.enqueue(neighbor)
  122. // 如果汇节点被标记层级,说明层次图构建完成
  123. return residualGraph.getLevel(sink) != -1
  124. function FindAugmentingPath(residualGraph, currentVertex, sink, minCapacity):
  125. // 如果当前节点等于汇节点,返回增广路径
  126. if currentVertex == sink:
  127. return [sink]
  128. // 遍历当前节点的邻居节点
  129. for each neighbor in residualGraph.getNeighbors(currentVertex):
  130. // 如果邻居节点的层级是当前节点的下一层级且残余容量大于0
  131. if residualGraph.getLevel(neighbor) == residualGraph.getLevel(currentVertex) + 1 and residualGraph.getResidualCapacity(currentVertex, neighbor) > 0:
  132. // 递归地查找从邻居节点到汇节点的增广路径
  133. augmentingPath = FindAugmentingPath(residualGraph, neighbor, sink, min(minCapacity, residualGraph.getResidualCapacity(currentVertex, neighbor)))
  134. // 如果找到增广路径,将当前节点添加到路径中并返回路径
  135. if augmentingPath is not None:
  136. augmentingPath.append(currentVertex)
  137. return augmentingPath
  138. // 如果没有找到增广路径,返回空
  139. return None
  140. function FindMinCapacity(augmentingPath):
  141. // 初始化最小残余容量为正无穷
  142. minCapacity = infinity
  143. // 遍历增广路径上的边,找到最小的残余容量
  144. for each edge (u, v) in augmentingPath:
  145. // 获取当前边的残余容量
  146. residualCapacity = residualGraph.getResidualCapacity(u, v)
  147. // 更新最小残余容量
  148. minCapacity = min(minCapacity, residualCapacity)
  149. // 返回最小残余容量
  150. return minCapacity
  151. function UpdateFlow(residualGraph, augmentingPath, minCapacity):
  152. // 在增广路径上更新每条边的流量
  153. for each edge (u, v) in augmentingPath:
  154. // 增加流量到正向边上
  155. residualGraph.increaseFlow(u, v, minCapacity)
  156. // 减少流量到反向边上
  157. residualGraph.decreaseFlow(v, u, minCapacity)

实验代码

first.cpp

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <limits>
  5. #include <queue>
  6. #include <cstdlib>
  7. #include <ctime>
  8. #include <unordered_set>
  9. using namespace std;
  10. // 生成指定范围内的随机整数
  11. int getRandomNumber(int minVal, int maxVal) {
  12. return minVal + rand() % (maxVal - minVal + 1);
  13. }
  14. // 随机生成医生的假日集合
  15. unordered_set<int> generateDoctorHolidays(int numHolidays, int numDays) {
  16. unordered_set<int> holidays;
  17. while (holidays.size() < numDays) {
  18. int holiday = getRandomNumber(1, numHolidays * numDays);
  19. holidays.insert(holiday);
  20. }
  21. return holidays;
  22. }
  23. // 边的数据结构
  24. struct Edge {
  25. int from;
  26. int to;
  27. int capacity;
  28. int flow;
  29. Edge(int from, int to, int capacity, int flow) : from(from), to(to), capacity(capacity), flow(flow) {}
  30. };
  31. // Dinic算法的实现
  32. class DinicAlgorithm {
  33. public:
  34. DinicAlgorithm(int numNodes) : numNodes(numNodes) {
  35. adjList.resize(numNodes);
  36. level.resize(numNodes);
  37. nextEdgeIndex.resize(numNodes);
  38. }
  39. void addEdge(int from, int to, int capacity) {
  40. adjList[from].push_back(edges.size());
  41. edges.push_back(Edge(from, to, capacity, 0));
  42. adjList[to].push_back(edges.size());
  43. edges.push_back(Edge(to, from, 0, 0));
  44. }
  45. bool bfs(int source, int sink) {
  46. fill(level.begin(), level.end(), -1);
  47. level[source] = 0;
  48. queue<int> q;
  49. q.push(source);
  50. while (!q.empty()) {
  51. int current = q.front();
  52. q.pop();
  53. for (int edgeIndex : adjList[current]) {
  54. Edge& edge = edges[edgeIndex];
  55. if (level[edge.to] == -1 && edge.capacity > edge.flow) {
  56. level[edge.to] = level[current] + 1;
  57. q.push(edge.to);
  58. }
  59. }
  60. }
  61. return level[sink] != -1;
  62. }
  63. int dfs(int current, int sink, int minCapacity) {
  64. if (current == sink) {
  65. return minCapacity;
  66. }
  67. while (nextEdgeIndex[current] < adjList[current].size()) {
  68. int edgeIndex = adjList[current][nextEdgeIndex[current]];
  69. Edge& edge = edges[edgeIndex];
  70. if (level[edge.to] == level[current] + 1 && edge.capacity > edge.flow) {
  71. int bottleneckCapacity = dfs(edge.to, sink, min(minCapacity, edge.capacity - edge.flow));
  72. if (bottleneckCapacity > 0) {
  73. edge.flow += bottleneckCapacity;
  74. edges[edgeIndex ^ 1].flow -= bottleneckCapacity;
  75. return bottleneckCapacity;
  76. }
  77. }
  78. nextEdgeIndex[current]++;
  79. }
  80. return 0;
  81. }
  82. int maxFlow(int source, int sink) {
  83. int totalFlow = 0;
  84. while (bfs(source, sink)) {
  85. fill(nextEdgeIndex.begin(), nextEdgeIndex.end(), 0);
  86. int flow;
  87. while ((flow = dfs(source, sink, numeric_limits<int>::max())) > 0) {
  88. totalFlow += flow;
  89. }
  90. }
  91. return totalFlow;
  92. }
  93. //private:
  94. int numNodes;
  95. vector<Edge> edges;
  96. vector<vector<int>> adjList;
  97. vector<int> level;
  98. vector<int> nextEdgeIndex;
  99. };
  100. // 主函数
  101. int main()
  102. {
  103. int numDoctors = 3;
  104. int numHolidays = 2;
  105. int numDays = 3;
  106. int maxDutyDays = 3;
  107. DinicAlgorithm dinic(numDoctors + numHolidays * numDays + 2); // 加2是为了添加源点和汇点
  108. int source = 0;
  109. int sink = numDoctors + numHolidays * numDays + 1;
  110. // 构建网络图
  111. for (int i = 1; i <= numDoctors; i++) {
  112. dinic.addEdge(source, i, maxDutyDays); // 源点到医生的边,容量为每名医生最多值班的天数
  113. }
  114. for (int i = 1; i <= numHolidays; i++) {
  115. for (int j = 1; j <= numDays; j++) {
  116. int holidayNode = numDoctors + (i - 1) * numDays + j;
  117. dinic.addEdge(holidayNode, sink, 1); // 假日到汇点的边,容量为1
  118. // 医生到假日的边,容量为1,根据医生的可值班假日集合确定边的连接关系
  119. // 医生n1的假日集合:{k1的第二天和第三天、k2的第二天和第三天}
  120. if (i == 1 && (j == 2 || j == 3)) {
  121. dinic.addEdge(1, holidayNode, 1);
  122. }
  123. // 医生n2的假日集合:{k1的第一天和第二天、k2的第一天和第二天}
  124. if (i == 1 && (j == 1 || j == 2)) {
  125. dinic.addEdge(2, holidayNode, 1);
  126. }
  127. // 医生n3的假日集合:{k1的第二天和第三天、k2的第一天和第二天}
  128. if ((i == 1 && (j == 2 || j == 3)) || (i == 2 && (j == 1 || j == 2))) {
  129. dinic.addEdge(3, holidayNode, 1);
  130. }
  131. }
  132. }
  133. // 求解最大流量
  134. int maxFlow = dinic.maxFlow(source, sink);
  135. cout << "Max flow: " << maxFlow << endl;
  136. // 输出分配方案
  137. for (int i = 0; i < dinic.adjList.size(); i++) {
  138. for (int edgeIndex : dinic.adjList[i]) {
  139. Edge& edge = dinic.edges[edgeIndex];
  140. if (edge.from != source && edge.to != sink && edge.flow > 0) {
  141. cout << "Doctor " << edge.from << " assigned to Holiday " << (edge.to - numDoctors - 1) / numDays + 1
  142. << ", Day " << (edge.to - numDoctors - 1) % numDays + 1 << endl;
  143. }
  144. }
  145. }
  146. return 0;
  147. }

 

medium.cpp

  1. #include <iostream>
  2. #include <vector>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <unordered_set>
  6. #include <queue>
  7. #include <algorithm>
  8. #include <unordered_map>
  9. #include<sstream>
  10. using namespace std;
  11. // Dinic算法中的边数据结构
  12. struct Edge {
  13. int from;
  14. int to;
  15. int capacity;
  16. int flow;
  17. Edge(int from, int to, int capacity) : from(from), to(to), capacity(capacity), flow(0) {}
  18. };
  19. // Dinic算法中的流网络数据结构
  20. class Dinic {
  21. public:
  22. int numNodes;
  23. vector<vector<int>> adjList;
  24. vector<Edge> edges;
  25. vector<int> level;
  26. vector<int> nextEdgeIndex;
  27. Dinic(int numNodes) : numNodes(numNodes), adjList(numNodes), level(numNodes), nextEdgeIndex(numNodes) {}
  28. void addEdge(int from, int to, int capacity) {
  29. adjList[from].push_back(edges.size());
  30. edges.emplace_back(from, to, capacity);
  31. adjList[to].push_back(edges.size());
  32. edges.emplace_back(to, from, 0);
  33. }
  34. bool bfs(int source, int sink) {
  35. fill(level.begin(), level.end(), -1);
  36. level[source] = 0;
  37. queue<int> q;
  38. q.push(source);
  39. while (!q.empty()) {
  40. int currentNode = q.front();
  41. q.pop();
  42. for (int edgeIndex : adjList[currentNode]) {
  43. Edge& edge = edges[edgeIndex];
  44. if (level[edge.to] == -1 && edge.capacity > edge.flow) {
  45. level[edge.to] = level[currentNode] + 1;
  46. q.push(edge.to);
  47. }
  48. }
  49. }
  50. return level[sink] != -1;
  51. }
  52. int dfs(int currentNode, int minCapacity, int sink) {
  53. if (currentNode == sink)
  54. return minCapacity;
  55. for (int& edgeIndex = nextEdgeIndex[currentNode]; edgeIndex < adjList[currentNode].size(); edgeIndex++) {
  56. Edge& edge = edges[adjList[currentNode][edgeIndex]];
  57. if (level[edge.to] == level[currentNode] + 1 && edge.capacity > edge.flow) {
  58. int bottleneck = dfs(edge.to, min(minCapacity, edge.capacity - edge.flow), sink);
  59. if (bottleneck > 0) {
  60. edge.flow += bottleneck;
  61. edges[adjList[currentNode][edgeIndex] ^ 1].flow -= bottleneck;
  62. return bottleneck;
  63. }
  64. }
  65. }
  66. return 0;
  67. }
  68. int maxFlow(int source, int sink) {
  69. int maxFlow = 0;
  70. while (bfs(source, sink)) {
  71. fill(nextEdgeIndex.begin(), nextEdgeIndex.end(), 0);
  72. int bottleneck;
  73. while ((bottleneck = dfs(source, INT_MAX, sink)) > 0) {
  74. maxFlow += bottleneck;
  75. }
  76. }
  77. return maxFlow;
  78. }
  79. };
  80. // 生成指定范围内的随机整数
  81. int getRandomNumber(int minVal, int maxVal) {
  82. return minVal + rand() % (maxVal - minVal + 1);
  83. }
  84. // 随机生成医生的假日集合
  85. unordered_set<int> generateDoctorHolidays(int numHolidays, int numDays) {
  86. unordered_set<int> holidays;
  87. while (holidays.size() < numDays) {
  88. int holiday = getRandomNumber(1, numHolidays * numDays);
  89. holidays.insert(holiday);
  90. }
  91. return holidays;
  92. }
  93. // 将假日的编号转换为具体的日期
  94. string getHolidayDate(int holiday, int numDaysPerMonth) {
  95. int month = (holiday - 1) / numDaysPerMonth + 1;
  96. int day = (holiday - 1) % numDaysPerMonth + 1;
  97. return to_string(month) + "/" + to_string(day);
  98. }
  99. // 输出医生的排班方案
  100. void printSchedule(const vector<unordered_map<int, string>>& schedule, int numDaysPerMonth) {
  101. for (int i = 0; i < schedule.size(); i++) {
  102. cout << "Doctor " << i + 1 << " schedule: ";
  103. for (const auto& entry : schedule[i]) {
  104. //cout << getHolidayDate(entry.first, numDaysPerMonth) << " - " << entry.second << " ";
  105. cout << getHolidayDate(entry.first, numDaysPerMonth) << " - " << entry.second << " ";
  106. }
  107. cout << endl;
  108. }
  109. }
  110. int main() {
  111. srand(static_cast<unsigned>(time(0))); // 设置随机数种子
  112. int numDoctors; // 医生数量
  113. int numHolidays; // 假期数量
  114. int numDays; // 每个假期的天数
  115. int maxDutyDays; // 每名医生最多值班的天数
  116. cout << "Enter the number of doctors: ";
  117. cin >> numDoctors;
  118. cout << "Enter the number of holidays: ";
  119. cin >> numHolidays;
  120. cout << "Enter the number of days per holiday: ";
  121. cin >> numDays;
  122. cout << "Enter the maximum duty days per doctor: ";
  123. cin >> maxDutyDays;
  124. // 生成每个医生的假日集合
  125. vector<unordered_set<int>> doctorHolidays(numDoctors);
  126. for (int i = 0; i < numDoctors; i++) {
  127. doctorHolidays[i] = generateDoctorHolidays(numHolidays, numDays);
  128. }
  129. // 输出生成的数据
  130. cout << "Number of doctors: " << numDoctors << endl;
  131. cout << "Number of holidays: " << numHolidays << endl;
  132. cout << "Number of days per holiday: " << numDays << endl;
  133. cout << "Maximum duty days per doctor: " << maxDutyDays << endl;
  134. for (int i = 0; i < numDoctors; i++) {
  135. cout << "Doctor " << i + 1 << " holidays: ";
  136. for (int holiday : doctorHolidays[i]) {
  137. cout << holiday << " ";
  138. }
  139. cout << endl;
  140. }
  141. // 构建流网络
  142. int numNodes = numDoctors + numHolidays * numDays + 2; // 医生节点 + 假期节点 + 源点 + 汇点
  143. int source = 0;
  144. int sink = numNodes - 1;
  145. Dinic dinic(numNodes);
  146. // 添加医生节点和源点之间的边
  147. for (int i = 1; i <= numDoctors; i++) {
  148. dinic.addEdge(source, i, maxDutyDays);
  149. }
  150. // 添加假期节点和汇点之间的边
  151. int nodeOffset = numDoctors;
  152. // 添加假期节点和汇点之间的边
  153. for (int i = 1; i <= numHolidays; i++) {
  154. for (int j = 1; j <= numDays; j++) {
  155. dinic.addEdge(nodeOffset + (i - 1) * numDays + j, sink, 1);
  156. }
  157. }
  158. // 添加医生节点和假期节点之间的边
  159. for (int i = 1; i <= numDoctors; i++) {
  160. for (int j = 1; j <= numHolidays; j++) {
  161. for (int k = 1; k <= numDays; k++) {
  162. if (doctorHolidays[i - 1].count((j - 1) * numDays + k) > 0) {
  163. dinic.addEdge(i, nodeOffset + (j - 1) * numDays + k, 1);
  164. }
  165. }
  166. }
  167. }
  168. int qi, zhi;
  169. qi = clock();
  170. // 使用Dinic算法求解最大流
  171. int maxFlow = dinic.maxFlow(source, sink);
  172. cout << "Maximum flow: " << maxFlow << endl;
  173. // 解析最大流,生成医生的排班方案
  174. vector<unordered_map<int, string>> schedule(numDoctors);
  175. for (const auto& edge : dinic.edges) {
  176. if (edge.from >= 1 && edge.from <= numDoctors && edge.to >= nodeOffset + 1) {
  177. if (edge.flow > 0) {
  178. int doctor = edge.from;
  179. int holiday = (edge.to - nodeOffset - 1) / numDays + 1;
  180. string date = getHolidayDate(edge.to - nodeOffset, numDays);
  181. schedule[doctor - 1][holiday] = date;
  182. }
  183. }
  184. }
  185. zhi = clock();
  186. // 输出医生的排班方案
  187. //printSchedule(schedule, numDays);
  188. cout << "耗时:" << zhi - qi << " ms" << endl;
  189. return 0;
  190. }

 

final.cpp

  1. #include <iostream>
  2. #include <vector>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <unordered_set>
  6. #include <queue>
  7. #include <algorithm>
  8. #include <unordered_map>
  9. #include<sstream>
  10. #include <chrono>
  11. using namespace std;
  12. using namespace std::chrono;
  13. // Dinic算法中的边数据结构
  14. struct Edge {
  15. int from;
  16. int to;
  17. int capacity;
  18. int flow;
  19. Edge(int from, int to, int capacity) : from(from), to(to), capacity(capacity), flow(0) {}
  20. };
  21. // Dinic算法中的流网络数据结构
  22. class Dinic {
  23. public:
  24. int numNodes;
  25. vector<vector<int>> adjList;
  26. vector<Edge> edges;
  27. vector<int> level;
  28. vector<int> nextEdgeIndex;
  29. Dinic(int numNodes) : numNodes(numNodes), adjList(numNodes), level(numNodes), nextEdgeIndex(numNodes) {}
  30. void addEdge(int from, int to, int capacity) {
  31. adjList[from].push_back(edges.size());
  32. edges.emplace_back(from, to, capacity);
  33. adjList[to].push_back(edges.size());
  34. edges.emplace_back(to, from, 0);
  35. }
  36. bool bfs(int source, int sink) {
  37. fill(level.begin(), level.end(), -1);
  38. level[source] = 0;
  39. queue<int> q;
  40. q.push(source);
  41. while (!q.empty()) {
  42. int currentNode = q.front();
  43. q.pop();
  44. for (int edgeIndex : adjList[currentNode]) {
  45. Edge& edge = edges[edgeIndex];
  46. if (level[edge.to] == -1 && edge.capacity > edge.flow) {
  47. level[edge.to] = level[currentNode] + 1;
  48. q.push(edge.to);
  49. }
  50. }
  51. }
  52. return level[sink] != -1;
  53. }
  54. int dfs(int currentNode, int minCapacity, int sink) {
  55. if (currentNode == sink)
  56. return minCapacity;
  57. for (int& edgeIndex = nextEdgeIndex[currentNode]; edgeIndex < adjList[currentNode].size(); edgeIndex++) {
  58. Edge& edge = edges[adjList[currentNode][edgeIndex]];
  59. if (level[edge.to] == level[currentNode] + 1 && edge.capacity > edge.flow) {
  60. int bottleneck = dfs(edge.to, min(minCapacity, edge.capacity - edge.flow), sink);
  61. if (bottleneck > 0) {
  62. edge.flow += bottleneck;
  63. edges[adjList[currentNode][edgeIndex] ^ 1].flow -= bottleneck;
  64. return bottleneck;
  65. }
  66. }
  67. }
  68. return 0;
  69. }
  70. int maxFlow(int source, int sink) {
  71. int maxFlow = 0;
  72. while (bfs(source, sink)) {
  73. fill(nextEdgeIndex.begin(), nextEdgeIndex.end(), 0);
  74. int bottleneck;
  75. while ((bottleneck = dfs(source, INT_MAX, sink)) > 0) {
  76. maxFlow += bottleneck;
  77. }
  78. }
  79. return maxFlow;
  80. }
  81. };
  82. int getRandomNumber(int minVal, int maxVal) {
  83. return minVal + rand() % (maxVal - minVal + 1);
  84. }
  85. unordered_set<int> generateDoctorHolidays(int numHolidays, int numDays) {
  86. unordered_set<int> holidays;
  87. while (holidays.size() < numDays) {
  88. int holiday = getRandomNumber(1, numHolidays * numDays);
  89. holidays.insert(holiday);
  90. }
  91. return holidays;
  92. }
  93. string getHolidayDate(int holiday, int numDaysPerMonth) {
  94. int month = (holiday - 1) / numDaysPerMonth + 1;
  95. int day = (holiday - 1) % numDaysPerMonth + 1;
  96. return to_string(month) + "/" + to_string(day);
  97. }
  98. void printSchedule(const vector<unordered_map<int, string>>& schedule, int numDaysPerMonth) {
  99. for (int i = 0; i < schedule.size(); i++) {
  100. cout << "Doctor " << i + 1 << " schedule: ";
  101. for (const auto& entry : schedule[i]) {
  102. //cout << getHolidayDate(entry.first, numDaysPerMonth) << " - " << entry.second << " ";
  103. cout << entry.first << " - " << entry.second << " ";
  104. }
  105. cout << endl;
  106. }
  107. }
  108. int main() {
  109. srand(static_cast<unsigned>(time(0))); // 设置随机数种子
  110. int numDoctors; // 医生数量
  111. int numHolidays; // 假期数量
  112. int numDays; // 每个假期的天数
  113. int maxDutyDays; // 每名医生最多值班的天数
  114. int mo;
  115. cout << "输入数据规模: ";
  116. cin >> mo;
  117. /*
  118. cout << "输入医生个数: ";
  119. cin >> numDoctors;
  120. cout << "输入假期个数: ";
  121. cin >> numHolidays;
  122. cout << "输入每个假期的天数: ";
  123. cin >> numDays;
  124. cout << "输入每名医生最多值班天数: ";
  125. cin >> maxDutyDays;
  126. */
  127. numDoctors = numHolidays = numDays = maxDutyDays = mo;
  128. cout << endl;
  129. // 生成每个医生的假日集合
  130. vector<unordered_set<int>> doctorHolidays(numDoctors);
  131. for (int i = 0; i < numDoctors; i++) {
  132. doctorHolidays[i] = generateDoctorHolidays(numHolidays, numDays);
  133. }
  134. // 输出生成的数据
  135. //cout << "Number of doctors: " << numDoctors << endl;
  136. //cout << "Number of holidays: " << numHolidays << endl;
  137. //cout << "Number of days per holiday: " << numDays << endl;
  138. //cout << "Maximum duty days per doctor: " << maxDutyDays << endl;
  139. for (int i = 0; i < numDoctors; i++) {
  140. cout << "Doctor " << i + 1 << " holidays: ";
  141. for (int holiday : doctorHolidays[i]) {
  142. cout << getHolidayDate(holiday, numDays) << " ";
  143. }
  144. cout << endl;
  145. }
  146. // 构建流网络
  147. int numNodes = numDoctors + numHolidays * numDays + 2; // 医生节点 + 假期节点 + 源点 + 汇点
  148. int source = 0;
  149. int sink = numNodes - 1;
  150. Dinic dinic(numNodes);
  151. // 添加医生节点和源点之间的边
  152. for (int i = 1; i <= numDoctors; i++) {
  153. dinic.addEdge(source, i, maxDutyDays);
  154. }
  155. // 添加假期节点和汇点之间的边
  156. int nodeOffset = numDoctors;
  157. // 添加假期节点和汇点之间的边
  158. for (int i = 1; i <= numHolidays; i++) {
  159. for (int j = 1; j <= numDays; j++) {
  160. dinic.addEdge(nodeOffset + (i - 1) * numDays + j, sink, 1);
  161. }
  162. }
  163. // 添加医生节点和假期节点之间的边
  164. for (int i = 1; i <= numDoctors; i++) {
  165. for (int j = 1; j <= numHolidays; j++) {
  166. for (int k = 1; k <= numDays; k++) {
  167. if (doctorHolidays[i - 1].count((j - 1) * numDays + k) > 0) {
  168. dinic.addEdge(i, nodeOffset + (j - 1) * numDays + k, 1);
  169. }
  170. }
  171. }
  172. }
  173. auto start = steady_clock::now();
  174. // 使用Dinic算法求解最大流
  175. int maxFlow = dinic.maxFlow(source, sink);
  176. auto end = steady_clock::now();
  177. cout << "Maximum flow: " << maxFlow << endl;
  178. // 解析最大流,生成医生的排班方案
  179. vector<unordered_map<int, string>> schedule(numDoctors);
  180. for (const auto& edge : dinic.edges) {
  181. if (edge.from >= 1 && edge.from <= numDoctors && edge.to >= nodeOffset + 1) {
  182. if (edge.flow > 0) {
  183. int doctor = edge.from;
  184. int holiday = (edge.to - nodeOffset - 1) / numDays + 1;
  185. string date = getHolidayDate(edge.to - nodeOffset, numDays);
  186. schedule[doctor - 1][holiday] = date;
  187. }
  188. }
  189. }
  190. auto last = duration_cast<microseconds>(end - start);
  191. // 输出医生的排班方案
  192. printSchedule(schedule, numDays);
  193. cout << endl << "耗时:" << last.count() * 0.001 << " ms" << endl;
  194. return 0;
  195. }

(by 归忆)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/621331
推荐阅读
相关标签
  

闽ICP备14008679号