洛谷P1073题(2009年NOIP提高组):最优贸易问题解析——SPFA算法的巧妙应用

一、问题背景
最优贸易问题要求在一个有向图中找到一条路径,使得在这条路径上某点买入、后续某点卖出时能获得最大利润。这是典型的图论问题,考察了图的遍历和动态规划思想。
二、算法选择
使用SPFA(Shortest Path Faster Algorithm)算法,它是Bellman-Ford算法的优化版本,适合处理带权有向图的最短路径问题。
三、完整代码
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;
struct Edge {
int to, cost;
};
vector<Edge> G[MAXN], rG[MAXN];
int min_price[MAXN], max_price[MAXN];
int price[MAXN];
bool vis[MAXN];
void spfa(int s, vector<Edge> graph[], int dist[], bool is_min) {
fill(dist, dist + MAXN, is_min ? INF : -INF);
fill(vis, vis + MAXN, false);
queue<int> q;
dist[s] = price[s];
q.push(s);
vis[s] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
vis[u] = false;
for (Edge &e : graph[u]) {
int v = e.to;
int new_val = is_min ? min(dist[u], price[v]) : max(dist[u], price[v]);
if ((is_min && new_val < dist[v]) || (!is_min && new_val > dist[v])) {
dist[v] = new_val;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> price[i];
}
for (int i = 0; i < m; ++i) {
int x, y, z;
cin >> x >> y >> z;
G[x].push_back({y, z});
rG[y].push_back({x, z});
if (z == 2) {
G[y].push_back({x, z});
rG[x].push_back({y, z});
}
}
spfa(1, G, min_price, true);
spfa(n, rG, max_price, false);
int ans = 0;
for (int i = 1; i <= n; ++i) {
if (min_price[i] != INF && max_price[i] != -INF) {
ans = max(ans, max_price[i] - min_price[i]);
}
}
cout << ans << endl;
return 0;
}四、关键思路解析
双向图处理:
正向图(G)用于从起点1出发计算最小买入价格
反向图(rG)用于从终点n出发计算最大卖出价格
SPFA变种应用:
传统SPFA用于求最短路径,这里改编用于求路径上的最小/最大值
第一次SPFA求从起点到各点的最小价格
第二次SPFA在反向图中求从终点到各点的最大价格
利润计算:
遍历所有节点,计算各点的(max_price - min_price)
取所有差值中的最大值即为答案
原创内容 转载请注明出处
