P1073 [NOIP 2009 提高组] 最优贸易(绿色 普及+/提高)
最长路径/最短路变种, https://www.luogu.com.cn/problem/P1073
本题原题数据极弱,Subtask 0 中的测试点为原题测试点,Subtask 1 中的测试点为 Hack 数据。
C 国有
C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C 国
假设 C 国有

假设
阿龙可以选择如下一条线路:
阿龙也可以选择如下一条线路:
现在给出
输入格式
第一行包含
第二行
接下来
输出格式
一个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出
输入输出样例 #1
输入 #1
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2输出 #1
5说明/提示
【数据范围】
输入数据保证
对于
对于
对于
对于
水晶球价格
NOIP 2009 提高组 第三题
这是一个经典的有向图最长路径/最短路变种问题。我们可以通过两次搜索(类似于 SPFA 或 Dijkstra 的松弛操作)来高效地解决。
解题思路
设
如果我们求出了这两个数组,那么对于任意一个既能从
因此,最终的答案就是
具体步骤:
- 求
:从起点 开始,在原图上进行类似 SPFA 的广度优先搜索。更新公式为: 。 - 求
:从终点 开始,在反向图上进行类似的广度优先搜索。更新公式为: 。 - 统计答案:遍历所有城市,计算最大差值。
由于水晶球的价格范围很小(
Python 3 实现
from collections import deque
import sys
def solve():
# 使用快速输入
input_data = sys.stdin.read().split()
if not input_data:
return
n = int(input_data[0])
m = int(input_data[1])
# 城市价格,下标从 1 开始
p = [0] + [int(x) for x in input_data[2 : 2 + n]]
# 建图:adj 为原图,rev_adj 为反向图
adj = [[] for _ in range(n + 1)]
rev_adj = [[] for _ in range(n + 1)]
idx = 2 + n
for _ in range(m):
u = int(input_data[idx])
v = int(input_data[idx + 1])
z = int(input_data[idx + 2])
idx += 3
adj[u].append(v)
rev_adj[v].append(u)
if z == 2:
adj[v].append(u)
rev_adj[u].append(v)
# 1. 求 min_p:从 1 出发在原图上跑 SPFA
min_p = [float("inf")] * (n + 1)
min_p[1] = p[1]
q = deque([1])
in_q = [False] * (n + 1)
in_q[1] = True
while q:
u = q.popleft()
in_q[u] = False
p_u = min_p[u]
for v in adj[u]:
val = p_u if p_u < p[v] else p[v]
if min_p[v] > val:
min_p[v] = val
if not in_q[v]:
q.append(v)
in_q[v] = True
# 2. 求 max_p:从 n 出发在反向图上跑 SPFA
max_p = [float("-inf")] * (n + 1)
max_p[n] = p[n]
q = deque([n])
in_q = [False] * (n + 1)
in_q[n] = True
while q:
u = q.popleft()
in_q[u] = False
p_u = max_p[u]
for v in rev_adj[u]:
val = p_u if p_u > p[v] else p[v]
if max_p[v] < val:
max_p[v] = val
if not in_q[v]:
q.append(v)
in_q[v] = True
# 3. 计算最大收益
ans = 0
for i in range(1, n + 1):
if min_p[i] != float("inf") and max_p[i] != float("-inf"):
ans = max(ans, max_p[i] - min_p[i])
print(ans)
if __name__ == "__main__":
solve()复杂度分析
- 时间复杂度:在最坏情况下,每个节点的值最多被更新
次(因为水晶球价格 )。因此,SPFA 的时间复杂度约为 ,在 Python 中运行效率良好,可顺利通过全部测试点(包括 Hack 数据)。 - 空间复杂度:
,主要用于存储邻接表和反向邻接表。