重返天梯-L3-028 森森旅游 (30 分) (堆优化dijkstra+multiset)

本文解析了一道关于路径规划与货币兑换的题目,介绍了如何通过堆优化Dijkstra算法计算在汇率变动下,从1号城市到n号城市的最低现金准备。涉及动态维护现金上限和旅游金转换,关键在于选择最优兑换点。

题目描述

重返天梯-L3-028 森森旅游 (30 分)原题链接
森森决定从 1 号城市出发,到 n 号城市去。他打算在出发前准备一些现金,并在途中的某个城市将剩余现金 全部 换成旅游金后继续旅游,直到到达 n 号城市为止。当然,他也可以选择在 1 号城市就兑换旅游金,或全部使用现金完成旅程。

对每一次汇率调整,在对应的一行中输出调整后森森至少需要准备多少现金,才能按他的计划从 1 号城市旅行到 n 号城市。

再次提醒:如果森森决定在途中的某个城市兑换旅游金,那么他必须将剩余现金全部、一次性兑换,剩下的旅途将完全使用旅游金支付。

题目分析

刚拿到题目的时候,在分析到底应该在哪个点全部兑换成旅游金。
我们将题目中描述的旅程分为两段,在p点之前,全部使用现金;在p点之后,全部使用旅游金。然后把每个点作为p点遍历一次就好了。

从数据范围判断出,要用堆优化djikstra( O ( m l o g n ) O(mlogn) O(mlogn))

分段计算现金,需要把后半段的旅游金按城市汇率进行上取整转换为现金(a/b=(a+b-1)/b)。用multiset进行维护,动态的删除保留最小值

  • s.erase(s.find(xxx))

C++

#include <iostream>
#include <cstring>
#include <set>
#include <queue>

using namespace std;

typedef long long LL;
typedef pair<LL, int> PII; // 距离要开成LL

const int N = 1e5 + 10, M = 200010 * 2; // 双向边
const LL INF = 0x3f3f3f3f3f3f3f3fll;

int n, m, q;
// 双图
int h1[N], h2[N], e[M], ne[M], w[M], idx;
LL dist1[N], dist2
### 关于L3-028 森森旅游的代码实现与题解 #### 问题析 题目描述了一个旅行场景,其中涉及从1号城市到n号城市的路径规划。旅途中可以选择在一个特定的城市将剩余现金换成旅游金,或者全程只使用一种货币完成旅程。为了找到优方案,需要考虑每种可能的情况并计算小费用。 此问题可以通过 **Dijkstra算法** 和 **反向建图** 的方法来解决。以下是详细的解析和代码实现: --- #### 解决思路 1. 构造正向图和反向图: - 正向图表示从当前城市前往其他城市的花费。 - 反向图用于快速查询从目标城市返回某城市的短距离[^2]。 2. 使用两次 Dijkstra 算法: - 第一次:从起点 `1` 到达任意城市的最短路径- 第二次:从终点 `n` 返回任意城市的最短路径(通过反向图)。 3. 计算总成本: - 对于每个城市 `k`,假设在此处兑换旅游金,则总成本为: \[ \text{cost} = f(1, k) + g(k, n) \] 其中 \(f(1, k)\) 表示从起点到城市 \(k\)最短路径,\(g(k, n)\) 表示从城市 \(k\) 到终点的最短路径。 4. 特殊情况处理: - 如果不兑换旅游金,则直接取 \(f(1, n)\) 即可。 终答案是从所有可能的成本中选取小值。 --- #### 实现代码 以下是一个基于 Python 的实现: ```python import heapq def dijkstra(graph, start, city_count): """执行Dijkstra算法""" dist = [float(&#39;inf&#39;)] * (city_count + 1) dist[start] = 0 pq = [(0, start)] # 小根 while pq: current_dist, u = heapq.heappop(pq) if current_dist > dist[u]: continue for v, weight in graph[u]: new_distance = current_dist + weight if new_distance < dist[v]: dist[v] = new_distance heapq.heappush(pq, (new_distance, v)) return dist # 输入读取 if __name__ == "__main__": import sys input = sys.stdin.read data = input().split() N, M = map(int, data[:2]) # 城市数N,边数M edges = [] idx = 2 for _ in range(M): a, b, c = map(int, data[idx:idx+3]) edges.append((a, b, c)) idx += 3 # 构建正向图和反向图 forward_graph = [[] for _ in range(N + 1)] reverse_graph = [[] for _ in range(N + 1)] for a, b, w in edges: forward_graph[a].append((b, w)) # 正向图 reverse_graph[b].append((a, w)) # 反向图 # 执行第一次Dijkstra(从1到各点) from_start = dijkstra(forward_graph, 1, N) # 执行第二次Dijkstra(从n到各点,利用反向图) to_end = dijkstra(reverse_graph, N, N) # 寻找小成本 min_cost = float(&#39;inf&#39;) for k in range(1, N + 1): # 遍历每一个可能的兑换点 cost = from_start[k] + to_end[k] if cost < min_cost: min_cost = cost # 输出结果 print(min_cost if min_cost != float(&#39;inf&#39;) else -1) ``` --- #### 复杂度析 1. 图构建时间复杂度为 \(O(M)\),其中 \(M\) 是边的数量。 2. 每次运行 Dijkstra 算法的时间复杂度为 \(O((N + M) \log N)\),因此总体时间为 \(O((N + M) \log N)\)--- #### 注意事项 - 数据范围较大时需优化输入/输出操作,建议使用标准库中的高效 IO 方法。 - 若某些节点不可达,应特别判断其距离是否仍为无穷大,并合理设置默认值。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeSlogan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值