7-4 森森旅游 (20 分)

这篇博客讨论了一个旅行者在Z省旅游时如何在现金和旅游金之间转换以最小化成本的问题。Z省提供了旅游金计划,允许在不同城市以不同汇率兑换旅游金。文章介绍了Dijkstra算法的应用,用于计算从1号城市到n号城市的最短路径,分别使用现金和旅游金。通过调整不同城市的汇率,计算旅行者所需最少现金。算法涉及了优先队列和多源最短路径计算,旨在找到最佳的现金兑换策略。

好久没出去旅游啦!森森决定去 Z 省旅游一下。

Z 省有 n 座城市(从 1 到 n 编号)以及 m 条连接两座城市的有向旅行线路(例如自驾、长途汽车、火车、飞机、轮船等),每次经过一条旅行线路时都需要支付该线路的费用(但这个收费标准可能不止一种,例如车票跟机票一般不是一个价格)。

Z 省为了鼓励大家在省内多逛逛,推出了旅游金计划:在 i 号城市可以用 1 元现金兑换 ai​ 元旅游金(只要现金足够,可以无限次兑换)。城市间的交通即可以使用现金支付路费,也可以用旅游金支付。具体来说,当通过第 j 条旅行线路时,可以用 cj​ 元现金 dj​ 元旅游金支付路费。注意: 每次只能选择一种支付方式,不可同时使用现金和旅游金混合支付。但对于不同的线路,旅客可以自由选择不同的支付方式。

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

Z 省政府会根据每个城市参与活动的情况调整汇率(即调整在某个城市 1 元现金能换多少旅游金)。现在你需要帮助森森计算一下,在每次调整之后最少需要携带多少现金才能完成他的旅程。

输入格式:

输入在第一行给出三个整数 n,m 与 q(1≤n≤105,1≤m≤2×105,1≤q≤105),依次表示城市的数量、旅行线路的数量以及汇率调整的次数。

接下来 m 行,每行给出四个整数 u,v,c 与 d(1≤u,v≤n,1≤c,d≤109),表示一条从 u 号城市通向 v 号城市的有向旅行线路。每次通过该线路需要支付 c 元现金或 d 元旅游金。数字间以空格分隔。输入保证从 1 号城市出发,一定可以通过若干条线路到达 n 号城市,但两城市间的旅行线路可能不止一条,对应不同的收费标准;也允许在城市内部游玩(即 u 和 v 相同)。

接下来的一行输入 n 个整数 a1​,a2​,⋯,an​(1≤ai​≤109),其中 ai​ 表示一开始在 i 号城市能用 1 元现金兑换 ai​ 个旅游金。数字间以空格分隔。

接下来 q 行描述汇率的调整。第 i 行输入两个整数 xi​ 与 ai′​(1≤xi​≤n,1≤ai′​≤109),表示第 i 次汇率调整后,xi​ 号城市能用 1 元现金兑换 ai′​ 个旅游金,而其它城市旅游金汇率不变。请注意:每次汇率调整都是在上一次汇率调整的基础上进行的。

输出格式:

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

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


#include<iostream>
#include<queue>
#include<vector>
#include<set>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, ll> PII;
const int N = 1e5 + 5;
ll INF = 1e18 + 1;//旅游先现金,再旅游金 
vector<PII> cash[N], travel[N];
bool st1[N], st2[N];
ll d1[N], d2[N], c, d, ai, a[N], trans[N];
int n, m, q, u, v, xi;
multiset<long long> minCost;
struct node {
	int id;
	ll dis;
	friend int operator < (const node& a, const node& b)//优先队列需要自定义<类型
	{
		return a.dis > b.dis;
	}
};
priority_queue<node> Q;
/*
vector <>cash[],任意一个vector 可以存储 初始结点的所有出度边的 尾结点与权重

1. ll d【】用来存储 初始结点到尾结点的 权重,利用松弛的特性一遍遍的读入,更新权重值
2.  st[] 表示结点是否被访问,访问过的结点不作为初始结点
1与2都是缔结斯科拉算法的核心内容
Dijkstra 算法主要做两件事情:
(1)从列表中找最值
(2)更新列表

*/
void DIJ(int start, vector<PII> cash[], ll d[], bool st[]) {
	fill(d, d + n + 1, INF);//fill[d,d+n+1),INF
	d[start] = 0;
	Q.push(node{ start,0 });
	while (Q.size()) 
	{
		int x = Q.top().id;
		Q.pop();
		if (st[x]) continue;
		st[x] = 1;
		for (int i = 0; i < cash[x].size(); i++) 
		{
			if (d[cash[x][i].x] > d[x] + cash[x][i].second) 
			{
				d[cash[x][i].x] = d[x] + cash[x][i].y;
				Q.push(node{ cash[x][i].x,d[cash[x][i].x] });
			}
		}
	}
}
int main()
{

	cin >> n >> m >> q;
	for (int i = 0; i < m; i++) {
		cin >> u >> v >> c >> d;
		cash[u].push_back({ v,c });
		travel[v].push_back({ u,d });
	}
	for (int i = 1; i <= n; i++) 
		cin >> a[i];

	DIJ(1, cash, d1, st1);//d1【】记录1结点到其余结点的最短的现金
	DIJ(n, travel, d2, st2);//d2【】记录n结点到其余结点的最短的旅游金,
	for (int i = 1; i <= n; i++) {//枚举中转点
		if (d1[i] == INF || d2[i] == INF) 
			continue;
		trans[i] = d1[i] + (d2[i] + a[i] - 1) / a[i];//比整数多就得多换一块钱,很巧的方法
		minCost.insert(trans[i]);//插入过后就以平衡二叉数的样子分布
	}
	for (int i = 0; i < q; i++) {
		cin >> xi >> ai;
		if (a[xi] == ai || !trans[xi]) 
			cout << *minCost.begin() << endl;
		else
		{
			minCost.erase(minCost.find(trans[xi]));
			a[xi] = ai;
			trans[xi] = d1[xi] + (d2[xi] + a[xi] - 1) / a[xi];//比整数多就得多换一块钱 
			minCost.insert(trans[xi]);
			cout << *minCost.begin() << endl;
		}
	}
	return 0;
}

set/multiset用法详解:https://blog.youkuaiyun.com/longshengguoji/article/details/8546286

### 关于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('inf')] * (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('inf') 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('inf') else -1) ``` --- #### 复杂度1. 图构建时间复杂度为 \(O(M)\),其中 \(M\) 是边的数量。 2. 每次运行 Dijkstra 算法的时间复杂度为 \(O((N + M) \log N)\),因此总体时间为 \(O((N + M) \log N)\)--- #### 注意事项 - 数据范围较大时需优化输入/输出操作,建议使用标准库中的高效 IO 方法。 - 若某些节点不可达,应特别判断其距离是否仍为无穷大,并合理设置默认值。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值