最大流最小割的概念可以参照以下网站,我这里给出一个实现,并举个例子说明
# 定义 Dinic 算法的类 class DinicGraph: def __init__(self, vertices): # 初始化具有指定顶点数的图。每个顶点有一个用于存储边的列表 self.V = vertices self.graph = [[] for _ in range(vertices)] def add_edge(self, s, t, capacity): # 向图中添加一条边。s和t是边的顶点,capacity是边的容量。 # 创建一条从s到t的边,容量为capacity,初始流量为0,记录反向边的索引 self.graph[s].append([t, capacity, 0, len(self.graph[t])]) # 创建反向边(从t到s),容量为0(反向流量),初始流量为0,记录正向边的索引 self.graph[t].append([s, 0, 0, len(self.graph[s]) - 1]) def bfs(self, s, t, level): # 使用广度优先搜索(BFS)构建层级图。判断是否可以从源点s到达汇点t。 for i in range(len(level)): level[i] = -1 # 初始化所有节点的层级为-1 level[s] = 0 # 源点的层级设为0 queue = [s] # BFS的队列,起始包含源点 while queue: u = queue.pop(0) for v, cap, flow, rev in self.graph[u]: # 如果节点v未访问过,且u到v的边未满载,则将v加入队列 if level[v] < 0 and flow < cap: level[v] = level[u] + 1 queue.append(v) return level[t] >= 0 # 如果汇点t被访问过,则返回True def dfs(self, u, t, flow, level, start): # 使用深度优先搜索(DFS)寻找增广路径。 # u是当前节点,t是汇点,flow是当前路径的流量,level是层级图,start记录搜索的起点 if u == t: return flow # 如果到达汇点,返回流量 for i in range(start[u], len(self.graph[u])): v, cap, flow_v, rev = self.graph[u][i] # 检查是否存在增广路径:v在下一层级,且u到v的边未满载 if level[v] == level[u] + 1 and flow_v < cap: cur_flow = min(flow, cap - flow_v) # 可增加的流量 temp_flow = self.dfs(v, t, cur_flow, level, start) # 递归调用DFS if temp_flow > 0: # 更新正向和反向边的流量 self.graph[u][i][2] += temp_flow self.graph[v][rev][2] -= temp_flow return temp_flow start[u] += 1 # 更新起点 return 0 # 如果没有增广路径,返回0 def dinic_max_flow(self, s, t): # 计算从源点s到汇点t的最大流 max_flow = 0 level = [-1] * self.V # 初始化层级图 while self.bfs(s, t, level): # 只要存在从s到t的路径 start = [0] * self.V # 初始化搜索的起点 while True: flow = self.dfs(s, t, float('inf'), level, start) # 通过DFS寻找增广路径 if flow == 0: break # 如果没有增广路径,结束循环 max_flow += flow # 累加流量 return max_flow # 返回最大流
以下实例代码
import matplotlib.pyplot as plt
import networkx as nx
# Compute the max flow
max_flow = g.dinic_max_flow(0, 5)
# Create a NetworkX graph for visualization
G = nx.DiGraph()
for u in range(len(g.graph)):
for v, cap, flow, _ in g.graph[u]:
if cap > 0:
G.add_edge(u, v, capacity=cap, flow=flow)
# Drawing the capacity graph
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
edge_labels = {(u, v): f"{d['capacity']}" for u, v, d in G.edges(data=True)}
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_color='skyblue', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.title('Capacity Graph')
# Drawing the flow graph
plt.subplot(1, 2, 2)
edge_labels = {(u, v): f"{d['flow']}" for u, v, d in G.edges(data=True)}
nx.draw(G, pos, with_labels=True, node_color='lightgreen', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.title(f'Flow Graph (Max Flow: {max_flow})')
plt.tight_layout()
plt.show()
以下左图为容量图,右图为最大流图。
这里展示了一个包含6个节点的网络的容量图和最大流图,这是基于上述的Dinic算法实现的:
容量图:这张图显示了网络中节点间边的原始容量。边上的数字代表每条边的容量。
流量图:这张图展示了从源点(节点0)到汇点(节点5)计算出的最大流后,每条边上的流量。边上的数字代表通过每条边的流量。算法计算出的这个网络的最大流量在图的标题中显示。
接下来计算最小割,以下是代码
def find_min_cut(dinic_graph, s):
"""
Find the minimum cut in the graph.
:param dinic_graph: The graph with maximum flow already computed.
:param s: The source vertex.
:return: A tuple containing the set of vertices reachable from s and the min-cut edges.
"""
# Perform a BFS from the source to find all reachable vertices
reachable = [False] * dinic_graph.V
queue = [s]
reachable[s] = True
while queue:
u = queue.pop(0)
for v, cap, flow, _ in dinic_graph.graph[u]:
# If there is remaining capacity and the vertex is not yet visited
if not reachable[v] and cap - flow > 0:
queue.append(v)
reachable[v] = True
# Find the edges that are from a reachable vertex to a non-reachable vertex
min_cut_edges = []
for u in range(dinic_graph.V):
for v, cap, flow, _ in dinic_graph.graph[u]:
if reachable[u] and not reachable[v] and cap > 0:
min_cut_edges.append((u, v))
return reachable, min_cut_edges
# Using the previously created DinicGraph instance 'g'
reachable, min_cut_edges = find_min_cut(g, 0)
# Create a NetworkX graph for the residual graph visualization
G_residual = nx.DiGraph()
for u in range(len(g.graph)):
for v, cap, flow, _ in g.graph[u]:
if cap > 0:
residual_capacity = cap - flow
if residual_capacity > 0: # Add edges with residual capacity
G_residual.add_edge(u, v, capacity=residual_capacity)
# Drawing the residual graph
plt.figure(figsize=(8, 6))
edge_labels = {(u, v): f"{d['capacity']}" for u, v, d in G_residual.edges(data=True)}
pos = nx.spring_layout(G_residual)
nx.draw(G_residual, pos, with_labels=True, node_color='lightblue', node_size=2000, edge_color='black', width=2)
nx.draw_networkx_edge_labels(G_residual, pos, edge_labels=edge_labels)
plt.title('Residual Graph after Max Flow')
plt.show()
reachable, min_cut_edges
以下是残差图
在计算了最大流后,我们可以找到网络的最小割。以下是残差图和最小割的结果:
残差图:这张图显示了计算最大流后的残差图。每条边上的数字表示该边的残余容量(原始容量减去流量)。只有残余容量大于0的边被显示。
最小割结果:最小割是一组边,其移除会断开源点到汇点的所有路径。在这个例子中,最小割包含的边是:(1, 3), (4, 3), (4, 5)。这意味着通过切断这些边,我们可以阻断从源点到汇点的所有路径。
在图中,可以看到源点可以到达的节点(1, 2, 4)和它不能到达的节点(3, 5)。最小割的边正是从可达节点到不可达节点的那些边。