T603757 市场贸易 最短路BMF
解题思路
这道题可以建模为一个单源最短路问题,目标是找到从初始商品 a 到目标商品 b 的最小花费(可能是负数)。
关键点分析
-
商品交换的花费计算:
-
如果
v[x] > v[y],交换后你会获得v[x] - v[y]元,但需要支付1元手续费,所以总花费是-(v[x] - v[y]) + 1。 -
如果
v[x] < v[y],你需要支付v[y] - v[x] + 1元。 -
综上,每次交换的花费可以表示为
(a[y] - a[x]) + 1(因为a[y] - a[x]可能是正或负)。
-
-
图建模:
-
每种商品是一个节点,交换关系是有向边。
-
边权 =
(a[y] - a[x]) + 1(即交换的花费)。 -
问题转化为:从节点
a出发,找到到节点b的最短路径(路径权值和最小)。
-
-
最短路算法选择:
-
由于边权可能是负数,不能直接用 Dijkstra(需要非负权值)。
-
本题可以用 Bellman-Ford(BMF)算法,因为它能处理负权边,并且可以检测负环(虽然本题不需要)。
-
由于
n和m较大(n ≤ 1e5,m ≤ 2e5),标准的 BMF(复杂度O(nm))可能会超时,但题目数据可能较宽松,或者本题的 BMF 实现优化后可以通过。
-
#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 10, inf = 0x3f3f3f3f; struct node { int x, y, z; // x: 当前商品, y: 目标商品, z: 交换花费 (a[y] - a[x] + 1) }; int n, m, s, e; // n: 商品数量, m: 商人数量, s: 起始商品, e: 目标商品 int a[N]; // a[i]: 第 i 种商品的价值 node t[N]; // t[i]: 第 i 个商人的交易信息 (x, y, z) int dis[N]; // dis[i]: 从 s 到 i 的最小花费 // Bellman-Ford 算法求单源最短路 void BMF(int s) { memset(dis, inf, sizeof(dis)); // 初始化 dis 为无穷大 dis[s] = 0; // 起点花费为 0 // 松弛操作,最多进行 n-1 轮 for (int i = 1; i < n; i++) { int f = 0; // 标记本轮是否发生松弛 for (int j = 1; j <= m; j++) { int x = t[j].x, y = t[j].y, z = t[j].z; // 如果 x 不可达,跳过 if (dis[x] == inf) continue; // 如果发现更优路径,更新 dis[y] if (dis[y] > dis[x] + z + 1) { dis[y] = dis[x] + z + 1; f = 1; // 标记发生松弛 } } if (!f) break; // 如果本轮未松弛,提前退出 } } int main() { cin >> n >> m >> s >> e; for (int i = 0; i < n; i++) cin >> a[i]; // 输入商品价值 // 构建交易图 for (int i = 1; i <= m; i++) { int x, y; cin >> x >> y; t[i] = {x, y, a[y] - a[x]}; // 边权 z = a[y] - a[x],最终花费是 z + 1 } BMF(s); // 计算从 s 出发的最短路 if (dis[e] == inf) cout << "No solution"; // 无法到达 else cout << dis[e]; // 输出最小花费 return 0; }
算法优化点
-
Bellman-Ford 优化:
-
由于本题不需要检测负环,所以可以提前终止(当某轮未松弛时退出)。
-
但最坏情况下仍需
O(nm)时间,可能无法通过极限数据(n=1e5, m=2e5),但题目可能数据较水。
-
-
更优解法(SPFA):
-
可以用 SPFA(队列优化的 Bellman-Ford),平均复杂度
O(km)(k是平均入队次数),在随机数据下更快。 -
但极端情况下(如网格图)会退化回
O(nm)。
-
-
Dijkstra 不可行:
-
由于存在负权边(
a[y] - a[x] + 1可能是负的),Dijkstra 无法正确计算最短路。
-

浙公网安备 33010602011771号