import sys
from collections import deque
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
class DinicSourceSinkVisual:
def __init__(self, n, edges, source, sink, visualize=True):
"""
:param n: 节点数
:param edges: 边列表 [(u, v, lb, ub)]
:param source: 源点
:param sink: 汇点
:param visualize: 是否可视化
"""
self.n = n
self.source = source
self.sink = sink
self.original_edges = edges
self.visualize = visualize
self.fig, self.ax = plt.subplots(figsize=(14, 10))
self.fig.suptitle("有源汇上下界可行流算法动态可视化", fontsize=16)
# 初始化超级源汇
self.super_source = n
self.super_sink = n + 1
self.total_nodes = n + 2
# 计算每个节点的流量差
self.A = [0] * (n + 2)
for u, v, lb, ub in edges:
self.A[u] -= lb
self.A[v] += lb
# 添加源汇到超级源汇的边
self.A[self.source] += float('inf') # 源点需要无限供应
self.A[self.sink] -= float('inf') # 汇点需要无限消耗
# 创建Dinic数据结构
self.graph = [[] for _ in range(self.total_nodes)]
self.level = [-1] * self.total_nodes
self.cur = [0] * self.total_nodes
self.edge_info = {} # 存储边信息
# 添加原图中的边
self.original_edge_refs = []
for i, (u, v, lb, ub) in enumerate(edges):
cap = ub - lb
# 添加边并记录信息
self.add_edge(u, v, cap, (i, lb, ub, f"e{i}"))
self.original_edge_refs.append((u, v, len(self.graph[u]) - 1, lb))
# 添加超级源汇的边
self.total_flow = 0
for i in range(n):
if self.A[i] > 0:
self.add_edge(self.super_source, i, self.A[i], (f"S→{i}", "super_source"))
self.total_flow += self.A[i]
elif self.A[i] < 0:
self.add_edge(i, self.super_sink, -self.A[i], (f"{i}→T", "super_sink"))
# 初始化可视化
if self.visualize:
self.initialize_visualization()
def add_edge(self, u, v, cap, info=None):
"""添加边并存储信息"""
forward = [v, cap, 0, info] # [目标, 容量, 流量, 信息]
reverse = [u, 0, 0, None] # 反向边
forward[2] = reverse
reverse[2] = forward
self.graph[u].append(forward)
self.graph[v].append(reverse)
# 存储边信息用于可视化
if info:
self.edge_info[(u, v)] = {
'capacity': cap,
'flow': 0,
'info': info
}
return forward
def bfs(self):
"""BFS分层并可视化"""
self.level = [-1] * self.total_nodes
queue = deque([self.super_source])
self.level[self.super_source] = 0
# 可视化:显示BFS搜索过程
if self.visualize:
self.visualize_step(f"BFS分层: 访问超级源点S (L0)")
plt.pause(1.0)
while queue:
u = queue.popleft()
for i, edge in enumerate(self.graph[u]):
v, cap, rev, info = edge
if cap > 0 and self.level[v] == -1:
self.level[v] = self.level[u] + 1
queue.append(v)
# 可视化:显示新访问的节点
if self.visualize:
if v < self.n:
node_label = f"节点{v}"
elif v == self.super_sink:
node_label = "超级汇点T"
else:
node_label = "超级源点S"
self.visualize_step(f"BFS分层: 访问{node_label} (L{self.level[v]})")
plt.pause(0.3)
return self.level[self.super_sink] != -1
def dfs(self, u, t, flow, path=None):
"""DFS查找增广路径并可视化"""
if path is None:
path = []
if u == t:
# 可视化:显示找到的增广路径
if self.visualize:
path_desc = "→".join(
[f"{'S' if p == self.super_source else 'T' if p == self.super_sink else p}"
for p in path + [t]]
)
self.visualize_step(f"找到增广路径: {path_desc}\n流量: {flow}")
plt.pause(1.5)
return flow
for i in range(self.cur[u], len(self.graph[u])):
self.cur[u] = i
edge = self.graph[u][i]
v, cap, rev, info = edge
if cap > 0 and self.level[v] == self.level[u] + 1:
# 可视化:显示当前探索的边
if self.visualize:
edge_desc = self.get_edge_description(u, v)
self.visualize_step(f"探索: {edge_desc} (剩余容量: {cap})")
plt.pause(0.5)
f = self.dfs(v, t, min(flow, cap), path + [u])
if f > 0:
# 更新边流量
edge[1] -= f
rev[1] += f
# 更新可视化信息
if (u, v) in self.edge_info:
self.edge_info[(u, v)]['flow'] += f
elif (v, u) in self.edge_info: # 处理反向边
self.edge_info[(v, u)]['flow'] -= f
# 可视化:显示流量更新
if self.visualize:
edge_desc = self.get_edge_description(u, v)
self.visualize_step(f"更新: {edge_desc}\n增加流量: {f}")
plt.pause(0.8)
return f
return 0
def max_flow(self):
"""计算最大流并动态可视化"""
total_flow = 0
iteration = 1
while self.bfs():
self.cur = [0] * self.total_nodes
if self.visualize:
self.visualize_step(f"开始阶段 {iteration} (分层完成)")
plt.pause(1.0)
while True:
flow = self.dfs(self.super_source, self.super_sink, float('inf'))
if flow == 0:
break
total_flow += flow
if self.visualize:
self.visualize_step(f"阶段 {iteration} 完成\n累计流量: {total_flow}/{self.total_flow}")
plt.pause(1.0)
iteration += 1
# 检查可行解
if total_flow != self.total_flow:
if self.visualize:
self.visualize_step(f"无可行解!\n需求流量: {self.total_flow}, 实际流量: {total_flow}")
plt.pause(3.0)
return None
# 计算原图中每条边的实际流量
flows = []
for u, v, idx, lb in self.original_edge_refs:
edge = self.graph[u][idx]
flow_in_additional = (edge[3][2] - edge[3][1]) - edge[1]
actual_flow = lb + flow_in_additional
flows.append(actual_flow)
if self.visualize:
self.visualize_final_flow(flows)
plt.pause(5.0)
return flows
def get_edge_description(self, u, v):
"""获取边的描述信息"""
if u == self.super_source:
return f"S → {v}"
elif v == self.super_sink:
return f"{u} → T"
elif u == self.source and v == self.sink:
return f"{u}→{v} (源汇边)"
elif (u, v) in self.edge_info:
info = self.edge_info[(u, v)]['info']
if isinstance(info, tuple) and len(info) > 3:
return f"{u} → {v} ({info[3]})"
return f"{u} → {v}"
def initialize_visualization(self):
"""初始化可视化布局"""
self.G = nx.DiGraph()
# 添加节点
for i in range(self.n):
self.G.add_node(i, label=f"{i}")
self.G.add_node(self.super_source, label="S")
self.G.add_node(self.super_sink, label="T")
# 添加边
for u in range(self.total_nodes):
for edge in self.graph[u]:
v, cap, _, info = edge
if cap > 0: # 只添加正向边
self.G.add_edge(u, v, capacity=cap, flow=0)
# 创建环形布局
self.pos = {}
# 普通节点布置在圆上
angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False)
for i in range(self.n):
angle = angles[i]
self.pos[i] = (np.cos(angle), np.sin(angle))
# 特殊节点位置
self.pos[self.source] = (0, 1.2) # 源点在上方
self.pos[self.sink] = (0, -1.2) # 汇点在下方
self.pos[self.super_source] = (-1.5, 0) # 超级源点在左侧
self.pos[self.super_sink] = (1.5, 0) # 超级汇点在右侧
# 初始绘图
self.ax.clear()
# 节点颜色:普通节点-浅蓝,源汇点-浅绿,超级源汇-浅红
node_colors = []
for node in self.G.nodes():
if node == self.source or node == self.sink:
node_colors.append('lightgreen')
elif node == self.super_source or node == self.super_sink:
node_colors.append('salmon')
else:
node_colors.append('lightblue')
nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=node_colors)
nx.draw_networkx_labels(self.G, self.pos,
labels={n: d['label'] for n, d in self.G.nodes(data=True)})
# 绘制边
self.edge_collection = nx.draw_networkx_edges(
self.G, self.pos, arrowstyle='->', arrowsize=20,
edge_color='gray', width=1, ax=self.ax
)
# 初始化边标签
self.edge_labels = {}
for u, v in self.G.edges():
self.edge_labels[(u, v)] = self.ax.text(0, 0, "",
fontsize=8, ha='center', va='center')
self.ax.set_title("初始化网络", fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.pause(2.0)
def visualize_step(self, message):
"""可视化当前步骤"""
self.ax.clear()
# 节点颜色
node_colors = []
for node in self.G.nodes():
if node == self.source or node == self.sink:
node_colors.append('lightgreen')
elif node == self.super_source or node == self.super_sink:
node_colors.append('salmon')
else:
node_colors.append('lightblue')
# 绘制节点
nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=node_colors)
nx.draw_networkx_labels(self.G, self.pos,
labels={n: d['label'] for n, d in self.G.nodes(data=True)})
# 绘制边并设置颜色和宽度
edge_colors = []
edge_widths = []
for u, v in self.G.edges():
# 获取当前边的状态
cap = self.G[u][v]['capacity']
flow = self.edge_info.get((u, v), {}).get('flow', 0)
# 计算饱和度
saturation = flow / cap if cap > 0 else 0
# 使用颜色表示饱和度
edge_colors.append(plt.cm.RdYlGn(saturation))
# 使用宽度表示流量
edge_widths.append(1 + 3 * saturation)
# 绘制边
nx.draw_networkx_edges(
self.G, self.pos, arrowstyle='->', arrowsize=20,
edge_color=edge_colors, width=edge_widths, ax=self.ax
)
# 更新边标签
for (u, v), text in self.edge_labels.items():
# 获取边信息
cap = self.G[u][v]['capacity']
flow = self.edge_info.get((u, v), {}).get('flow', 0)
# 特殊边处理
if u == self.super_source or v == self.super_sink:
label = f"{flow}/{cap}"
else:
# 获取原始边信息
info = self.edge_info.get((u, v), {}).get('info', None)
if info and isinstance(info, tuple):
_, lb, ub, name = info
actual_flow = lb + flow
label = f"{name}: {actual_flow}/{ub}\n[{lb},{ub}]"
else:
label = f"{flow}/{cap}"
# 计算边的中点位置
x = (self.pos[u][0] + self.pos[v][0]) / 2
y = (self.pos[u][1] + self.pos[v][1]) / 2
# 更新文本位置和内容
text.set_position((x, y))
text.set_text(label)
self.ax.add_artist(text)
# 显示当前信息
self.ax.set_title(message, fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.draw()
def visualize_final_flow(self, flows):
"""可视化最终可行流分配"""
self.ax.clear()
# 只绘制原图节点
H = nx.DiGraph()
for i in range(self.n):
H.add_node(i, label=f"{i}")
# 添加原图边
for i, (u, v, lb, ub) in enumerate(self.original_edges):
H.add_edge(u, v, flow=flows[i], lb=lb, ub=ub, name=f"e{i}")
# 特殊布局:源点在上,汇点在下,其他节点在中间
pos = {}
# 源点位置
pos[self.source] = (0, 2)
# 汇点位置
pos[self.sink] = (0, -2)
# 其他节点均匀分布
core_nodes = [i for i in range(self.n) if i != self.source and i != self.sink]
for idx, node in enumerate(core_nodes):
pos[node] = (idx - len(core_nodes) / 2, 0)
# 绘制节点
node_colors = ['lightgreen' if node == self.source or node == self.sink
else 'lightblue' for node in H.nodes()]
nx.draw_networkx_nodes(H, pos, node_size=800, node_color=node_colors)
nx.draw_networkx_labels(H, pos)
# 绘制边
edge_colors = []
edge_widths = []
for u, v in H.edges():
flow = H[u][v]['flow']
ub = H[u][v]['ub']
saturation = flow / ub
edge_colors.append(plt.cm.RdYlGn(saturation))
edge_widths.append(1 + 3 * saturation)
nx.draw_networkx_edges(
H, pos, arrowstyle='->', arrowsize=20,
edge_color=edge_colors, width=edge_widths, ax=self.ax
)
# 添加边标签
edge_labels = {}
for u, v in H.edges():
flow = H[u][v]['flow']
lb = H[u][v]['lb']
ub = H[u][v]['ub']
name = H[u][v]['name']
edge_labels[(u, v)] = f"{name}: {flow}\n[{lb},{ub}]"
nx.draw_networkx_edge_labels(H, pos, edge_labels=edge_labels, font_size=8)
self.ax.set_title("可行流分配结果", fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.draw()
def circulation_flow_visual(n, edges, source, sink):
"""有源汇上下界可行流求解与可视化"""
# 创建可视化实例
dinic_visual = DinicSourceSinkVisual(n, edges, source, sink, visualize=True)
# 计算可行流
flows = dinic_visual.max_flow()
if flows is None:
print("无可行流解")
return None
print("\n各边实际流量分配:")
for i, (u, v, lb, ub) in enumerate(edges):
print(f"边 {u}→{v} ({lb},{ub}): {flows[i]}")
# 计算源点到汇点的总流量
source_flow = sum(flows[i] for i, (u, v, _, _) in enumerate(edges) if u == source)
sink_flow = sum(flows[i] for i, (u, v, _, _) in enumerate(edges) if v == sink)
print(f"\n源点({source})总输出流量: {source_flow}")
print(f"汇点({sink})总输入流量: {sink_flow}")
plt.show() # 保持窗口打开
return flows
if __name__ == "__main__":
# 15节点有解网络示例
print("=" * 50)
print("15节点网络的有源汇上下界可行流计算(有解示例)")
# 定义15节点网络的边(调整后的有解版本)
n = 15
edges = [
# 核心环状结构 (0-4) - 平衡设计
(0, 1, 2, 5), (1, 2, 2, 5), (2, 3, 2, 5), (3, 4, 2, 5), (4, 0, 2, 5),
(0, 3, 1, 4), (2, 4, 1, 4), (4, 1, 1, 4),
# 中间层级 (5-9) - 对称平衡设计
(0, 5, 1, 4), (1, 6, 1, 4), (2, 7, 1, 4), (3, 8, 1, 4), (4, 9, 1, 4),
(5, 6, 1, 4), (6, 7, 1, 4), (7, 8, 1, 4), (8, 9, 1, 4), (9, 5, 1, 4),
(5, 7, 1, 3), (6, 8, 1, 3), (7, 9, 1, 3), (8, 5, 1, 3), (9, 6, 1, 3),
# 外围节点 (10-14) - 小流量连接
(5, 10, 1, 3), (6, 11, 1, 3), (7, 12, 1, 3), (8, 13, 1, 3), (9, 14, 1, 3),
(10, 11, 1, 3), (11, 12, 1, 3), (12, 13, 1, 3), (13, 14, 1, 3), (14, 10, 1, 3),
(10, 12, 1, 2), (11, 13, 1, 2), (12, 14, 1, 2), (13, 10, 1, 2), (14, 11, 1, 2),
# 添加补偿边解决核心节点不平衡
(0, 10, 1, 3), # 为节点0提供额外流出
(10, 1, 1, 3), # 为节点1提供额外流入
(10, 2, 1, 3), # 为节点2提供额外流入
(10, 3, 1, 3), # 为节点3提供额外流入
(10, 4, 1, 3), # 为节点4提供额外流入
(11, 5, 1, 3), # 为节点5提供额外流入
(11, 6, 1, 3), # 为节点6提供额外流入
(11, 7, 1, 3), # 为节点7提供额外流入
(11, 8, 1, 3), # 为节点8提供额外流入
(11, 9, 1, 3), # 为节点9提供额外流入
]
# 设置源点和汇点
source = 0 # 节点0作为源点
sink = 9 # 节点9作为汇点
# 计算并可视化可行流
flows = circulation_flow_visual(n, edges, source, sink)
上面是数据在无源汇问题中有解,但是在这个问题中没有解,请给出一个有解是15节点示例,并验证