无源汇上下界可行流

本文介绍了一种解决上下界网络流问题的方法,通过构造初始流及其残量网络,并利用附加流来调整流量守恒条件,最终转化为最大流问题求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

%%%liu_runda

前置技能

最大流

定义

上下界网络流流:每条边的流量除了上界还有下界。
无源汇上下界可行流:没有源汇的上下界网络流。因为只需要满足每个点流量守恒,所以一般求可行流。

求解方法

令每条边的流量等于流量下界,得到一个初始流,然后建出这个流的残量网络。

因为初始流的流量不一定守恒,所以我们考虑建一个附加流,使得这个附加流加上初始流之后达到守恒。就像这样:
如果某个点在初始流中满足流量守恒,那么这个点在附加流中也满足流量守恒。
如果某个点在初始流中流入量比流出量多 x x ,那么这个点在附加流中的流出量比流入量少x
如果某个点在初始流中流入量比流出量少 x x ,那么这个店在附加流中的流出量比流入量多x

而实际上,我们只需要建一个源点 ss s s 和汇点 tt t t 。对于第二种情况,连一条 ss>i s s − > i 的容量为 x x 的边。第三种情况,连一条i>tt的容量为 x − x 的边。如果存在一种可行流使得 ss>tt s s − > t t 满流,那么说明存在可行流。而满流就是求解最大流。此时原网络中每条边的流量就是下界+这条边的流量。

模板

ZOJ2314

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 205
#define M ((N*(N+1))<<1)
#define F inline
#define inf 0x7fffffff
using namespace std;
struct edge{ int next,to,v,flow; }ed[M];
int n,m,k,t,ss,tt,sum,ans; bool f[N];
int h[N],l[M],cp[N],dis[N],que[N],A[N];
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
F void writec(int x){ if (x>9) writec(x/10); putchar(x%10+48); }
F void _write(int x){ writec(x),putchar('\n'); }
//以上为IO优化
F void addedge(int x,int y,int z){
    ed[k]=(edge){h[x],y,z,0},h[x]=k++;
    ed[k]=(edge){h[y],x,0,0},h[y]=k++;
}
F bool bfs(){
    memset(f,false,sizeof(f));
    int r=0,w=1; dis[ss]=0,f[ss]=true,que[1]=ss;
    while (r<w)
        for (int x=que[++r],i=h[x],v;~i;i=ed[i].next)
            if (!f[v=ed[i].to]&&ed[i].v>ed[i].flow)
                dis[v]=dis[x]+1,f[v]=true,que[++w]=v;
    return f[tt];
}
int dfs(int x,int rem){
    if (x==tt||!rem) return rem; int sum=0;
    for (int &i=cp[x];~i;i=ed[i].next)
        if (dis[ed[i].to]==dis[x]+1){
            int p=dfs(ed[i].to,min(rem,ed[i].v-ed[i].flow));
            if (p) sum+=p,ed[i].flow+=p,ed[i^1].flow-=p,rem-=p;
            if (!rem) break;
        }
    return sum;
}
F int mf(){
    int ans=0;
    while (bfs())
        memcpy(cp,h,sizeof(h)),ans+=dfs(ss,inf);
    return ans;
}
//以上为Dinic板子
int main(){
    for (t=_read();t;t--){
        n=_read(),m=_read(),ss=0,tt=n+1;
        memset(h,-1,sizeof(h)),k=sum=0;
        memset(A,0,sizeof(A));//A[]存的就是x
        for (int i=1,x,y,z;i<=m;i++){
            x=_read(),y=_read(),l[i]=_read(),z=_read();
            A[x]-=l[i],A[y]+=l[i],addedge(x,y,z-l[i]);//初始流
        }
        for (int i=1;i<=n;i++)//附加流
            if (A[i]>0) addedge(ss,i,A[i]),sum+=A[i];
            else if (A[i]<0) addedge(i,tt,-A[i]);
        if (mf()!=sum) puts("NO");//是否满流
        else{
            puts("YES");
            for (int i=0;i<(m<<1);i+=2)
                _write(ed[i].flow+l[i/2+1]);
        }
        if (t>1) putchar('\n');
    }
}
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 DinicVisual: def __init__(self, n, edges, visualize=True): """ :param n: 节点数 :param edges: 边列表 [(u, v, lb, ub)] :param visualize: 是否可视化 """ self.n = n self.original_edges = edges self.visualize = visualize self.fig, self.ax = plt.subplots(figsize=(14, 10)) self.fig.suptitle("无上下界可行算法动态可视化", fontsize=16) # 初始化超级 self.s = n self.t = n + 1 self.total_nodes = n + 2 # 计算每个节点的量差 self.A = [0] * n for u, v, lb, ub in edges: self.A[u] -= lb self.A[v] += lb # 创建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.s, i, self.A[i], (f"s→{i}", "source")) self.total_flow += self.A[i] elif self.A[i] < 0: self.add_edge(i, self.t, -self.A[i], (f"{i}→t", "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.s]) self.level[self.s] = 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}" else: node_label = "点t" if v == self.t else "点s" self.visualize_step(f"BFS分层: 访问{node_label} (L{self.level[v]})") plt.pause(0.3) return self.level[self.t] != -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.s else 't' if p == self.t 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.s, self.t, 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.s: return f"s → {v}" elif v == self.t: return f"{u} → t" 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.s, label="s") self.G.add_node(self.t, 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.s] = (0, 1.5) # 上方 self.pos[self.t] = (0, -1.5) # 下方 # 初始绘图 self.ax.clear() nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()]) 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() # 绘制节点 nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()]) 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.s or v == self.t: 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}") # 环形布局 angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False) pos = {} for i in range(self.n): angle = angles[i] pos[i] = (np.cos(angle), np.sin(angle)) # 绘制节点 nx.draw_networkx_nodes(H, pos, node_size=800, node_color='lightgreen') 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): """无上下界可行求解与可视化""" # 创建可视化实例 dinic_visual = DinicVisual(n, edges, 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]}") plt.show() # 保持窗口打开 return flows if __name__ == "__main__": # 15节点复杂网络示例 print("=" * 50) print("15节点复杂网络的无上下界可行计算") # 定义15节点网络的边 n = 15 # 节点数 edges = [ # 核心环状结构 (0-4) (0, 1, 2, 5), (1, 2, 1, 4), (2, 3, 3, 6), (3, 4, 2, 5), (4, 0, 1, 3), (0, 3, 1, 4), (2, 0, 2, 5), # 中间层级连接 (5-9) (1, 5, 1, 3), (5, 6, 2, 4), (6, 7, 1, 3), (7, 8, 3, 6), (8, 9, 2, 5), (9, 2, 1, 4), (3, 7, 1, 3), (4, 8, 2, 5), # 外围节点连接 (10-14) (5, 10, 1, 3), (10, 11, 2, 4), (11, 12, 1, 3), (6, 11, 1, 4), (7, 13, 2, 5), (13, 14, 1, 3), (14, 9, 1, 4), (12, 8, 2, 5), (12, 14, 1, 3) ] # 计算并可视化可行 flows = circulation_flow_visual(n, edges) 代码运行结果是 C:\Users\25827\.conda\envs\torch\python.exe C:\Users\25827\Desktop\图论代码\无上下界可行.py ================================================== 15节点复杂网络的无上下界可行计算 无可行解 进程已结束,退出代码为 0 1.给出一个有解的例子,但是网络框架不变 2.增强访问和探索可视化(比如访问路径) 给出修改后完整全程代码(没有省略)
最新发布
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值