最优贸易
思路
设我们可以以\(i\)为分界点,分别求经过\([1, i]\)的路径的最低价格,和\([i, n]\)的最高价格, 相减便可以得到以以\(i\)为分解点的最短路径,这样遍历所有分解点可以得到答案, 对于上述做法左右两边都是可以任意走的,也可以当天买当天卖, 那么可以用dp来求吗,显然图中可能存在环路,所以dp的方法不可行,那么我们可以考虑Dijkstra或SPFA, 对于Dijkstra显然也是不可行的,因为贪心解可能不是正解。
如下, 4号点更新后不会在被更新,所以我们考虑使用SPFA。
SPFA的话肯定遍历\(n - 1\)条边, 那么保证了途中会经过n个点,那么我们寸图的时候记录一个反向边, 然后开两个数组求最优值即可。 平均复杂度\(O(M)\)
Code
#include <iostream>
#include <cstring>
#include <queue>
using i64 = long long;
const int N = 1e5 + 10, M = 1e6 + 10;
int n, m;
int hs[N], ht[N], e[M], ne[M], w[N], idx;
int dmin[N], dmax[N];
bool st[N];
void add(int h[], int a, int b) {
ne[idx] = h[a], h[a] = idx, e[idx ++] = b;
}
void spfa(int h[], int dist[], int type) {
std::queue<int> q1;
if (type) {
memset(dist, -0x3f, sizeof dmax);
dist[n] = w[n];
q1.push(n);
} else {
memset(dist, 0x3f, sizeof dmin);
dist[1] = w[1];
q1.push(1);
}
while (q1.size()) {
int t = q1.front(); q1.pop();
st[t] = false;
// std::cout << t << "\n";
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (type == 1 && dist[j] < std::max(w[j], dist[t])) {
dist[j] = std::max(w[j], dist[t]);
q1.push(j);
if (!st[j]) st[j] = true;
} else if (type == 0 && dist[j] > std::min(w[j], dist[t])) {
dist[j] = std::min(w[j], dist[t]);
q1.push(j);
if (!st[j]) st[j] = true;
}
}
}
}
int main() {
memset(hs, -1, sizeof hs);
memset(ht, -1, sizeof ht);
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) std::cin >> w[i];
for (int i = 1; i <= m; i ++) {
int x, y, z;
std::cin >> x >> y >> z;
add(hs, x, y), add(ht, y, x);
if (z == 2) add(hs, y, x), add(ht, x, y);
}
spfa(hs, dmin, 0);
spfa(ht, dmax, 1);
int res = 0;
for (int i = 1; i <= n; i ++) {
res = std::max(res, dmax[i] - dmin[i]);
}
std::cout << res;
}