P1266 速度限制(分层图spfa)

探讨了在部分限速标志缺失的情况下,如何通过分层图最短路径算法找到两个地点间最快路线的问题。该算法考虑了不同速度下的路径选择,以确保在遵守已知限速的前提下,达到目的地的时间最短。

P1266 速度限制

题目描述

在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线。开车时每条道路的限速成为最关键的问题。不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快。一种可以辩解的解决方案是,按照原来的速度行驶。你的任务是计算两地间的最快路线。

你将获得一份现代化城市的道路交通信息。为了使问题简化,地图只包括路口和道路。每条道路是有向的,只连接了两条道路,并且最多只有一块限速标志,位于路的起点。两地A和B,最多只有一条道路从A连接到B。你可以假设加速能够在瞬间完成并且不会有交通堵塞等情况影响你。当然,你的车速不能超过当前的速度限制。

输入输出格式

输入格式:

 

第一行是3个整数N,M和D(2<=N<=150),表示道路的数目,用0..N-1标记。M是道路的总数,D表示你的目的地。

接下来的M行,每行描述一条道路,每行有4个整数A(0≤A<N),B(0≤B<N),V(0≤V≤500)and L(1≤L≤500),这条路是从A到B的,速度限制是V,长度为L。如果V是0,表示这条路的限速未知。

如果V不为0,则经过该路的时间T=L/V。否则T=L/Vold,Vold是你到达该路口前的速度。开始时你位于0点,并且速度为70。

 

输出格式:

 

输出文件仅一行整数,表示从0到D经过的城市。

输出的顺序必须按照你经过这些城市的顺序,以0开始,以D结束。仅有一条最快路线。

 

输入输出样例

输入样例#1: 复制
6 15 1
0 1 25 68
0 2 30 50
0 5 0 101
1 2 70 77
1 3 35 42
2 0 0 22
2 1 40 86
2 3 0 23
2 4 45 40
3 1 64 14
3 5 0 23
4 1 95 8
5 1 0 84
5 2 90 64
5 3 36 40
输出样例#1: 复制
0 5 2 3 1

/*
分层图最短路
dis[i][j] 到i速度为j
数组开大!!!!!!!!!! 
*/
#include<bits/stdc++.h>

#define N 5007

using namespace std;
int head[N],size;
int n,m,d,vis[N][N];
double dis[N][N];
struct edge{
    int v,to,next;double w;
}e[100007];
struct node{int u,v;};
node pre[N][N];

void add(int x,int y,int v,double w)
{
    e[++size].to=y;
    e[size].next=head[x];
    head[x]=size;
    e[size].w=w;e[size].v=v;
}

void spfa()
{
     queue<node>q;
     q.push((node){0,70});
     dis[0][70]=0;vis[0][70]=1;
     while(q.empty()!=1)
     {
            node x=q.front();q.pop(); 
            int u=x.u,sp=x.v;vis[u][sp]=0;
            for(int i=head[u];i;i=e[i].next)
            {
                int tp=0,v=e[i].to;
                tp=(e[i].v?e[i].v:sp);
                if(tp)
                if(dis[v][tp]>dis[u][sp]+(e[i].w*1.0/tp*1.0))
                {
                    pre[v][tp]=x;
                    dis[v][tp]=dis[u][sp]+(e[i].w*1.0/tp*1.0);
                    if(!vis[v][tp])
                    {
                        q.push((node){v,tp});
                        vis[v][tp]=1;
                    }
                }
            }
      }
}

void print(int x,int speed)
{
    if(x!=0)
    print(pre[x][speed].u,pre[x][speed].v);
    cout<<x<<' '; return;
}

int main()
{
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=n;i++)
    for(int j=0;j<=500;j++)
    dis[i][j]=19260817;
    for(int i=1;i<=m;i++)
    {
        int x,y,v; double w;
        scanf("%d%d%d%lf",&x,&y,&v,&w);
        add(x,y,v,w);
    }
    spfa();
    double now=192608173;
    int ans;
    for(int i=1;i<=500;i++)
    {
        if(dis[d][i]<now)
        now=dis[d][i],ans=i;
    }
    print(d,ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/L-Memory/p/9754596.html

#include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn = 1005; const int maxm = 100005; const int inf = 0x3f3f3f3f; struct Edge { int to, next, w, cost; } lst[maxm]; int len = 0, first[maxn]; int dis[maxn], vis[maxn], pre[maxn]; queue<int> q; void addEdge(int a, int b, int w, int cost) { lst[len].to = b; lst[len].next = first[a]; lst[len].w = w; lst[len].cost = cost; first[a] = len++; lst[len].to = a; lst[len].next = first[b]; lst[len].w = 0; lst[len].cost = -cost; first[b] = len++; } bool spfa(int s, int t) { memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; vis[s] = 1; q.push(s); while (!q.empty()) { int x = q.front(); q.pop(); vis[x] = 0; for (int pt = first[x]; pt != -1; pt = lst[pt].next) { if (lst[pt].w && dis[lst[pt].to] > dis[x] + lst[pt].cost) { dis[lst[pt].to] = dis[x] + lst[pt].cost; pre[lst[pt].to] = pt; if (!vis[lst[pt].to]) { vis[lst[pt].to] = 1; q.push(lst[pt].to); } } } } return dis[t] != inf; } int minCostMaxFlow(int s, int t, int &cost) { int flow = 0; cost = 0; while (spfa(s, t)) { int min_flow = inf; for (int pt = pre[t]; pt != -1; pt = pre[lst[pt ^ 1].to]) { if (lst[pt].w < min_flow) min_flow = lst[pt].w; } for (int pt = pre[t]; pt != -1; pt = pre[lst[pt ^ 1].to]) { lst[pt].w -= min_flow; lst[pt ^ 1].w += min_flow; cost += lst[pt].cost * min_flow; } flow += min_flow; } return flow; } int main() { int n, m, s, t; scanf("%d%d%d%d", &n, &m, &s, &t); memset(first, -1, sizeof(first)); len = 0; for (int i = 0; i < m; ++i) { int a, b, w, cost; scanf("%d%d%d%d", &a, &b, &w, &cost); addEdge(a, b, w, cost); } int cost = 0; minCostMaxFlow(s, t, cost); printf("Max Flow: %d, Min Cost: %d\n", cost, cost); return 0; } 参考上面的C++代码,修改下面python代码,实现上下界最小费用最大流动态可视化过程 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节点复杂网络的无源汇上下界可行流计算(保证有解)") 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提供额外流入 ] # 计算并可视化可行流 flows = circulation_flow_visual(n, edges)
最新发布
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值