min_cost_flow

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>

using namespace std;


struct edge {
    int to;     //要到的点
    int cap;    //容量
    int cost;   //单位费用
    int rev;    //反向边
};

int V;                          //顶点数
vector<edge> G[MAXV];           //图的邻接表
int dis[MAXV];                  //最短距离
int prevv[MAXV], preve[MAXV];   //最短路中的前驱节点和对应的边

//添加边
void add_edge(int from, int to, int cap, int cost) {
    G[from].push_back((edge){to, cap, cost, G[to].size()});         //正向边
    G[to].push_back((edge){from, 0, -cost, G[from].size() - 1});    //反向边
}


//求s到t流量为f的最小费用流
//如果不能再增广则返回-1
int min_cost_flow(int s, int t, int f) {
    int res = 0;
    while(f > 0) {
        //利用Bellman-Ford算法求s到t的最短路
        fill(dis, dis + V, INF);
        dis[s] = 0;
        bool update = true;
        while(update) {
            update = false;
            for(int v = 0; v < V; ++v) {
                if(dis[v] == INF)
                    continue;
                for(int i = 0; i < G[v].size(); ++i) {
                    edge& e = G[v][i];
                    if(e.cap > 0 && dis[e.to] > dis[v] + e.cost) {
                        dis[e.to] = dis[v] + e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }
        if(dis[t] == INF)   //不能再增广了
            return -1;

        //沿s到t的最短路尽量增广
        int d = f;
        for(int v = t; v != s; v = prevv[v])
            d = min(d, G[prevv[v]][preve[v]].cap);
        f -= d;
        res += d * dis[t];
        for(int v = t; v != s; v = prevv[v]) {
            edge& e = G[prevv[v]][preve[v]];
            e.cap -= d;
            //cost += d * e.cost;???????????????
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

import sys import random import heapq 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 StochasticNetworkFlowOptimizer: def __init__(self, n, edges, source, sink, visualize=True, stochastic_demands=None): """ :param n: 节点数 :param edges: 边列表 [(u, v, lb, ub, cost)] :param source: 源点 :param sink: 汇点 :param visualize: 是否可视化 :param stochastic_demands: 随机需求字典 {节点: (均值, 标准差)} """ self.n = n self.source = source self.sink = sink self.original_edges = edges.copy() self.visualize = visualize self.stochastic_demands = stochastic_demands or {} if 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, cost in self.original_edges: self.A[u] -= lb self.A[v] += lb # 添加随机需求 for node, (mean, std) in self.stochastic_demands.items(): # 使用均值作为初始需求 self.A[node] += mean # 添加源汇之间无限容量的边 edges.append((sink, source, 0, float('inf'), 0)) # 创建图数据结构 self.graph = [[] for _ in range(self.total_nodes)] self.dist = [float('inf')] * self.total_nodes self.vis = [False] * self.total_nodes self.pre = [-1] * self.total_nodes self.edge_info = {} self.total_cost = 0 self.iteration_count = 0 self.max_flow_value = 0 # 添加图中的边 self.edge_refs = [] for i, (u, v, lb, ub, cost) in enumerate(edges): cap = ub - lb self.add_edge(u, v, cap, cost, (i, lb, ub, cost, f"e{i}")) if i < len(edges) - 1: self.edge_refs.append((u, v, len(self.graph[u]) - 1, lb)) # 添加超级源汇的边 self.total_flow = 0 for i in range(n + 2): if self.A[i] > 0: self.add_edge(self.super_source, i, self.A[i], 0, (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], 0, (f"{i}→T", "super_sink")) # 初始化可视化 if visualize: self.initialize_visualization() def add_edge(self, u, v, cap, cost, info=None): """添加边并存储信息""" edge_data = { 'capacity': cap, 'cost': cost, 'flow': 0, 'info': info } forward = [v, cap, cost, None, edge_data] reverse = [u, 0, -cost, None, None] forward[3] = reverse reverse[3] = forward self.graph[u].append(forward) self.graph[v].append(reverse) if info: self.edge_info[(u, v)] = edge_data return forward def spfa(self, s, t): """SPFA算法寻找最小费用增广路径""" self.dist = [float('inf')] * self.total_nodes self.vis = [False] * self.total_nodes self.pre = [-1] * self.total_nodes self.dist[s] = 0 self.vis[s] = True queue = deque([s]) if self.visualize: self.visualize_step(f"SPFA: 寻找最小费用路径 (迭代 {self.iteration_count})") while queue: u = queue.popleft() self.vis[u] = False for idx, edge in enumerate(self.graph[u]): v, cap, cost, rev, edge_data = edge if cap > 0 and self.dist[u] + cost < self.dist[v]: self.dist[v] = self.dist[u] + cost self.pre[v] = (u, idx) if not self.vis[v]: self.vis[v] = True queue.append(v) return self.dist[t] < float('inf') def min_cost_flow(self): """计算最小费用流""" total_flow = 0 self.iteration_count = 0 while self.spfa(self.super_source, self.super_sink): flow = float('inf') cur = self.super_sink path_nodes = [] # 计算增广路径上的最小容量 while cur != self.super_source: u, idx = self.pre[cur] edge = self.graph[u][idx] path_nodes.append(cur) flow = min(flow, edge[1]) cur = u path_nodes.append(self.super_source) path_nodes.reverse() # 更新增广路径上的流量 cur = self.super_sink while cur != self.super_source: u, idx = self.pre[cur] edge = self.graph[u][idx] rev_edge = edge[3] edge[1] -= flow rev_edge[1] += flow if edge[4]: edge[4]['flow'] += flow self.total_cost += flow * edge[2] # 更新可视化信息 if (u, cur) in self.edge_info: self.edge_info[(u, cur)]['flow'] += flow elif (cur, u) in self.edge_info: self.edge_info[(cur, u)]['flow'] -= flow cur = u total_flow += flow self.iteration_count += 1 if self.visualize: self.visualize_step(f"更新流量: {flow} (总费用: {self.total_cost})") # 检查可行解 if total_flow != self.total_flow: if self.visualize: self.visualize_step(f"无可行解! 需求流量: {self.total_flow}, 实际流量: {total_flow}") return None, None # 计算原图中每条边的实际流量 flows = [] for u, v, idx, lb in self.edge_refs: if u == self.sink and v == self.source: continue edge = self.graph[u][idx] actual_flow = lb + (self.edge_info[(u, v)]['capacity'] - edge[1]) flows.append(actual_flow) self.max_flow_value = sum(flows) if self.visualize: self.visualize_final_flow(flows) return flows, self.total_cost def monte_carlo_simulation(self, num_samples=100): """蒙特卡洛模拟随机需求""" if not self.stochastic_demands: print("没有随机需求,无需模拟") return self.min_cost_flow() results = [] print(f"开始蒙特卡洛模拟 ({num_samples} 次迭代)") for i in range(num_samples): # 生成随机需求 for node, (mean, std) in self.stochastic_demands.items(): demand = max(0, int(np.random.normal(mean, std))) # 更新节点流量差 self.A[node] += demand - self.stochastic_demands[node][0] # 重新计算超级源汇的边 self.update_super_edges() # 计算最小费用流 flows, cost = self.min_cost_flow() if flows is not None: results.append({ 'flows': flows, 'cost': cost, 'demands': self.A.copy() }) # 重置需求为均值 for node, (mean, std) in self.stochastic_demands.items(): self.A[node] += self.stochastic_demands[node][0] - demand return results def update_super_edges(self): """更新超级源汇的边以反映需求变化""" # 移除旧的超级源汇边 self.graph[self.super_source] = [] self.graph[self.super_sink] = [] # 添加新的超级源汇边 self.total_flow = 0 for i in range(self.n + 2): if self.A[i] > 0: self.add_edge(self.super_source, i, self.A[i], 0, (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], 0, (f"{i}→T", "super_sink")) def optimize_robust_flow(self, robustness_level=0.1): """鲁棒优化:考虑需求波动""" print(f"执行鲁棒优化 (鲁棒性级别: {robustness_level})") # 调整需求以考虑最坏情况 for node, (mean, std) in self.stochastic_demands.items(): # 增加需求以考虑不确定性 adjusted_demand = mean + robustness_level * std # 更新节点流量差 self.A[node] += adjusted_demand - mean # 更新超级源汇的边 self.update_super_edges() # 计算最小费用流 flows, cost = self.min_cost_flow() return flows, cost def get_node_label(self, node): """获取节点标签""" if node == self.super_source: return "S" elif node == self.super_sink: return "T" elif node == self.source: return f"源点({node})" elif node == self.sink: return f"汇点({node})" else: return f"{node}" 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, cost, rev, edge_data = edge if cap > 0: self.G.add_edge(u, v, capacity=cap, cost=cost, 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.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'] cost = self.G[u][v]['cost'] flow = self.edge_info.get((u, v), {}).get('flow', 0) if u == self.super_source or v == self.super_sink: label = f"{flow}/{cap}\n费用:0" else: info = self.edge_info.get((u, v), {}).get('info', None) if info and isinstance(info, tuple): _, lb, ub, cost_val, name = info actual_flow = lb + flow label = f"{name}: {actual_flow}/{ub}\n费用:{cost_val}\n[{lb},{ub}]" else: label = f"{flow}/{cap}\n费用:{cost}" 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) # 显示当前信息 title = f"{message}\n总费用: {self.total_cost}" if self.stochastic_demands: title += f"\n随机需求节点: {len(self.stochastic_demands)}" self.ax.set_title(title, fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.draw() plt.pause(0.5) 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, cost) in enumerate(self.original_edges): if i >= len(flows): continue H.add_edge(u, v, flow=flows[i], lb=lb, ub=ub, cost=cost, name=f"e{i}") # 使用原布局 pos = {k: v for k, v in self.pos.items() if k in H.nodes()} # 绘制节点 node_colors = [] for node in H.nodes(): if node in self.stochastic_demands: # 随机需求节点用特殊颜色 node_colors.append('gold') elif node == self.source or node == self.sink: node_colors.append('lightgreen') else: node_colors.append('lightblue') 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'] cost = H[u][v]['cost'] name = H[u][v]['name'] edge_labels[(u, v)] = f"{name}: {flow}\n费用:{cost}\n[{lb},{ub}]" nx.draw_networkx_edge_labels(H, pos, edge_labels=edge_labels, font_size=8) # 添加随机需求信息 demand_info = "\n随机需求节点:\n" for node, (mean, std) in self.stochastic_demands.items(): demand_info += f"节点 {node}: 均值={mean}, 标准差={std}\n" title = f"最优流分配 (总流量: {self.max_flow_value}, 总费用: {self.total_cost})\n{demand_info}" self.ax.set_title(title, fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.draw() def stochastic_flow_optimization(n, edges, source, sink, stochastic_demands=None, optimization_mode="min_cost", num_samples=100, robustness_level=0.1): """ 网络流随机优化求解 :param n: 节点数 :param edges: 边列表 [(u, v, lb, ub, cost)] :param source: 源点 :param sink: 汇点 :param stochastic_demands: 随机需求字典 {节点: (均值, 标准差)} :param optimization_mode: 优化模式 ("min_cost", "robust", "monte_carlo") :param num_samples: 蒙特卡洛模拟样本数 :param robustness_level: 鲁棒优化级别 :return: 优化结果 """ optimizer = StochasticNetworkFlowOptimizer( n, edges, source, sink, visualize=True, stochastic_demands=stochastic_demands ) if optimization_mode == "min_cost": print("执行最小费用流优化") flows, cost = optimizer.min_cost_flow() return {'flows': flows, 'cost': cost} elif optimization_mode == "robust": print("执行鲁棒优化") flows, cost = optimizer.optimize_robust_flow(robustness_level) return {'flows': flows, 'cost': cost} elif optimization_mode == "monte_carlo": print("执行蒙特卡洛模拟") results = optimizer.monte_carlo_simulation(num_samples) # 分析结果 if results: avg_cost = sum(r['cost'] for r in results) / len(results) min_cost = min(r['cost'] for r in results) max_cost = max(r['cost'] for r in results) print(f"蒙特卡洛结果: {len(results)}次模拟, 平均费用={avg_cost:.2f}, 最小={min_cost}, 最大={max_cost}") # 返回所有结果和统计信息 return { 'results': results, 'avg_cost': avg_cost, 'min_cost': min_cost, 'max_cost': max_cost } return None else: raise ValueError(f"未知优化模式: {optimization_mode}") if __name__ == "__main__": # 复杂网络示例 (15节点) print("=" * 50) print("网络流随机优化问题求解") n = 15 edges = [ # 源点→核心节点 (u, v, lb, ub, cost) (0, 1, 5, 20, 2), (0, 2, 5, 20, 3), # 核心环状结构 (1, 2, 0, 10, 1), (2, 3, 2, 15, 4), (3, 4, 2, 15, 2), (4, 1, 0, 10, 1), # 核心→中间节点 (1, 5, 1, 10, 3), (2, 6, 1, 10, 2), (3, 7, 1, 10, 4), (4, 8, 1, 10, 3), # 中间层平衡结构 (5, 6, 0, 10, 1), (6, 7, 0, 10, 2), (7, 8, 0, 10, 1), (8, 5, 0, 10, 3), # 中间→汇点 (5, 14, 3, 15, 5), (6, 14, 3, 15, 4), (7, 14, 2, 15, 3), (8, 14, 2, 15, 2), # 连接外围节点 (1, 9, 0, 10, 2), (2, 10, 0, 10, 3), (3, 11, 0, 10, 1), (4, 12, 0, 10, 2), (9, 13, 0, 10, 4), (10, 13, 0, 10, 2), (11, 13, 0, 10, 3), (12, 13, 0, 10, 1), (13, 14, 0, 20, 2) # 汇点入口 ] # 设置源点和汇点 source = 0 sink = 14 # 设置随机需求 (节点: (均值, 标准差)) stochastic_demands = { 5: (8, 2), # 节点5有随机需求 7: (6, 1.5), # 节点7有随机需求 10: (4, 1), # 节点10有随机需求 12: (5, 1.2) # 节点12有随机需求 } # 选择优化模式 optimization_mode = "robust" # 可选: "min_cost", "robust", "monte_carlo" # 执行优化 result = stochastic_flow_optimization( n, edges, source, sink, stochastic_demands=stochastic_demands, optimization_mode=optimization_mode, num_samples=100, robustness_level=0.2 ) if result: if optimization_mode == "monte_carlo": print(f"蒙特卡洛模拟结果: 平均费用={result['avg_cost']:.2f}") else: print(f"优化结果: 总费用={result['cost']}") plt.show() # 保持窗口打开 C:\Users\25827\.conda\envs\torch\python.exe C:\Users\25827\Desktop\图论代码\网络流随机优化.py ================================================== 网络流随机优化问题求解 执行鲁棒优化 执行鲁棒优化 (鲁棒性级别: 0.2) 优化结果: 总费用=None 进程已结束,退出代码为 0 有问题吗,是不是太简单了
最新发布
06-15
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 MinCostFlowSourceSinkVisual: def __init__(self, n, edges, source, sink, visualize=True): """ :param n: 节点数 :param edges: 边列表 [(u, v, lb, ub, cost)] :param source: 源点 :param sink: 汇点 :param visualize: 是否可视化 """ self.n = n self.source = source self.sink = sink self.original_edges = edges.copy() # 保存原始边 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, cost in self.original_edges: self.A[u] -= lb self.A[v] += lb # 添加源汇之间无限容量的边 edges.append((sink, source, 0, float('inf'), 0)) # 创建最小费用流数据结构 self.graph = [[] for _ in range(self.total_nodes)] self.dist = [float('inf')] * self.total_nodes self.vis = [False] * self.total_nodes self.pre = [-1] * self.total_nodes self.edge_info = {} # 存储边信息 self.total_cost = 0 # 总费用 # 添加图中的边 self.edge_refs = [] for i, (u, v, lb, ub, cost) in enumerate(edges): cap = ub - lb # 添加边并记录信息 self.add_edge(u, v, cap, cost, (i, lb, ub, cost, f"e{i}")) # 仅原始边(不包括后添加的sink->source边)记录在edge_refs中 if i < len(edges) - 1: # 最后一条是后添加的sink->source边 self.edge_refs.append((u, v, len(self.graph[u]) - 1, lb)) # 添加超级源汇的边 self.total_flow = 0 for i in range(n + 2): # 包含所有节点 if self.A[i] > 0: self.add_edge(self.super_source, i, self.A[i], 0, (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], 0, (f"{i}→T", "super_sink")) # 初始化可视化 if self.visualize: self.initialize_visualization() def add_edge(self, u, v, cap, cost, info=None): """添加边并存储信息""" forward = [v, cap, cost, 0, info] # [目标, 容量, 费用, 流量, 信息] reverse = [u, 0, -cost, 0, None] # 反向边 forward[3] = reverse reverse[3] = forward self.graph[u].append(forward) self.graph[v].append(reverse) # 存储边信息用于可视化 if info: self.edge_info[(u, v)] = { 'capacity': cap, 'cost': cost, 'flow': 0, 'info': info } return forward def spfa(self, s, t): """SPFA算法寻找最小费用增广路径""" self.dist = [float('inf')] * self.total_nodes self.vis = [False] * self.total_nodes self.pre = [-1] * self.total_nodes self.dist[s] = 0 self.vis[s] = True queue = deque([s]) # 可视化:显示SPFA开始 if self.visualize: self.visualize_step(f"SPFA: 从超级源点S开始寻找最小费用路径") plt.pause(0.5) while queue: u = queue.popleft() self.vis[u] = False for idx, edge in enumerate(self.graph[u]): v, cap, cost, rev, info = edge if cap > 0 and self.dist[u] + cost < self.dist[v]: self.dist[v] = self.dist[u] + cost self.pre[v] = (u, idx) # 记录前驱节点和边索引 # 可视化:更新节点距离 if self.visualize: node_label = self.get_node_label(v) self.visualize_step(f"SPFA: 更新 {node_label} 距离: {self.dist[v]}") plt.pause(0.3) if not self.vis[v]: self.vis[v] = True queue.append(v) return self.dist[t] < float('inf') def min_cost_flow(self): """计算最小费用流并动态可视化""" total_flow = 0 iteration = 1 while self.spfa(self.super_source, self.super_sink): # 计算增广路径上的最小容量 flow = float('inf') cur = self.super_sink path_nodes = [] while cur != self.super_source: u, idx = self.pre[cur] edge = self.graph[u][idx] path_nodes.append(cur) flow = min(flow, edge[1]) cur = u path_nodes.append(self.super_source) path_nodes.reverse() # 可视化:显示找到的增广路径 if self.visualize: path_desc = "→".join([self.get_node_label(n) for n in path_nodes]) self.visualize_step(f"找到增广路径: {path_desc}\n流量: {flow}, 费用: {self.dist[self.super_sink]}") plt.pause(1.5) # 更新增广路径上的流量 cur = self.super_sink path_edges = [] while cur != self.super_source: u, idx = self.pre[cur] edge = self.graph[u][idx] rev_edge = edge[3] # 更新边流量 edge[1] -= flow rev_edge[1] += flow edge[4] = edge[4] or {} # 确保info存在 edge[4]['flow'] = edge[4].get('flow', 0) + flow # 更新费用 self.total_cost += flow * edge[2] # 记录路径边用于可视化 path_edges.append((u, cur)) # 更新可视化信息 if (u, cur) in self.edge_info: self.edge_info[(u, cur)]['flow'] += flow elif (cur, u) in self.edge_info: # 处理反向边 self.edge_info[(cur, u)]['flow'] -= flow cur = u # 可视化:显示流量更新 if self.visualize: self.visualize_step(f"沿路径更新流量: {flow}\n累计费用: {self.total_cost}") plt.pause(0.8) total_flow += flow 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, None # 计算原图中每条边的实际流量 flows = [] for u, v, idx, lb in self.edge_refs: # 跳过最后添加的sink->source边 if u == self.sink and v == self.source: continue edge = self.graph[u][idx] actual_flow = lb + edge[1] # 实际流量 = 下界 + 残余网络中的剩余容量 flows.append(actual_flow) if self.visualize: self.visualize_final_flow(flows) plt.pause(5.0) return flows, self.total_cost def get_node_label(self, node): """获取节点标签""" if node == self.super_source: return "S" elif node == self.super_sink: return "T" elif node == self.source: return f"源点({node})" elif node == self.sink: return f"汇点({node})" else: return f"{node}" 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[4]})" 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, cost, _, info = edge if cap > 0: # 只添加正向边 self.G.add_edge(u, v, capacity=cap, cost=cost, 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'] cost = self.G[u][v]['cost'] flow = self.edge_info.get((u, v), {}).get('flow', 0) # 特殊边处理 if u == self.super_source or v == self.super_sink: label = f"{flow}/{cap}\n费用:0" else: # 获取原始边信息 info = self.edge_info.get((u, v), {}).get('info', None) if info and isinstance(info, tuple): _, lb, ub, cost_val, name = info actual_flow = lb + flow label = f"{name}: {actual_flow}/{ub}\n费用:{cost_val}\n[{lb},{ub}]" else: label = f"{flow}/{cap}\n费用:{cost}" # 计算边的中点位置 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(f"{message}\n总费用: {self.total_cost}", 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}") # 添加原图边(排除最后添加的sink->source边) for i, (u, v, lb, ub, cost) in enumerate(self.original_edges): if i >= len(flows): continue H.add_edge(u, v, flow=flows[i], lb=lb, ub=ub, cost=cost, name=f"e{i}") # 使用原布局,但只保留原图节点的位置 pos = {k: v for k, v in self.pos.items() if k in H.nodes()} # 绘制节点 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'] cost = H[u][v]['cost'] name = H[u][v]['name'] edge_labels[(u, v)] = f"{name}: {flow}\n费用:{cost}\n[{lb},{ub}]" nx.draw_networkx_edge_labels(H, pos, edge_labels=edge_labels, font_size=8) self.ax.set_title(f"最小费用流分配结果(总费用: {self.total_cost})", fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.draw() def min_cost_flow_visual(n, edges, source, sink): """有源汇上下界费用流求解与可视化""" # 创建可视化实例 mcf_visual = MinCostFlowSourceSinkVisual(n, edges, source, sink, visualize=True) # 计算最小费用流 flows, total_cost = mcf_visual.min_cost_flow() if flows is None: print("无可行流解") return None, None print("\n各边实际流量分配和费用:") for i, (u, v, lb, ub, cost) in enumerate(edges[:-1]): # 排除最后添加的sink->source边 print(f"边 {u}→{v} ({lb},{ub}): 流量={flows[i]}, 费用={cost}") # 计算源点到汇点的总流量 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}") print(f"总费用: {total_cost}") plt.show() # 保持窗口打开 return flows, total_cost if __name__ == "__main__": # 15节点有可行解的网络示例 - 简化版 print("=" * 50) print("15节点网络的有源汇上下界费用流计算 (保证有可行解)") # 简化设计:确保网络平衡 n = 15 edges = [ # 源点→核心节点 (u, v, lb, ub, cost) (0, 1, 5, 10, 2), (0, 2, 5, 10, 3), # 核心环状结构 (1, 2, 0, 5, 1), (2, 3, 2, 8, 4), (3, 4, 2, 8, 2), (4, 1, 0, 5, 1), # 核心→中间节点 (1, 5, 1, 4, 3), (2, 6, 1, 4, 2), (3, 7, 1, 4, 4), (4, 8, 1, 4, 3), # 中间层平衡结构 (5, 6, 0, 5, 1), (6, 7, 0, 5, 2), (7, 8, 0, 5, 1), (8, 5, 0, 5, 3), # 中间→汇点 (5, 14, 3, 6, 5), (6, 14, 3, 6, 4), (7, 14, 2, 5, 3), (8, 14, 2, 5, 2), # 连接外围节点 (1, 9, 0, 3, 2), (2, 10, 0, 3, 3), (3, 11, 0, 3, 1), (4, 12, 0, 3, 2), (9, 13, 0, 3, 4), (10, 13, 0, 3, 2), (11, 13, 0, 3, 3), (12, 13, 0, 3, 1), (13, 14, 0, 5, 2) # 汇点入口 ] # 设置源点和汇点 source = 0 # 节点0作为源点 sink = 14 # 节点14作为汇点 # 计算并可视化最小费用流 flows, total_cost = min_cost_flow_visual(n, edges, source, sink) C:\Users\25827\.conda\envs\torch\python.exe C:\Users\25827\Desktop\图论代码\有源汇上下界费用流.py ================================================== 15节点网络的有源汇上下界费用流计算 (保证有可行解) Traceback (most recent call last): File "C:\Users\25827\Desktop\图论代码\有源汇上下界费用流.py", line 516, in <module> flows, total_cost = min_cost_flow_visual(n, edges, source, sink) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\25827\Desktop\图论代码\有源汇上下界费用流.py", line 442, in min_cost_flow_visual flows, total_cost = mcf_visual.min_cost_flow() ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\25827\Desktop\图论代码\有源汇上下界费用流.py", line 165, in min_cost_flow edge[4]['flow'] = edge[4].get('flow', 0) + flow ^^^^^^^^^^^ AttributeError: 'tuple' object has no attribute 'get' 进程已结束,退出代码为 1 给出修改后的完整代码
06-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值