题目要求一个最大差值,然而买入必须要在卖出之前,也就是说某些单向边会影响答案
设dis1[i]为从起点到i点的最低价格,dis2[i]为终点到i点的最高价格(均包括i点),则dis2[i] - dis1[i]就是以i点为两次交易中间点时的收益
一个点能进行交易的条件是1能到这个点,然后这个点又能走到终点,若我们正向BFS可到达这个点,说明这个点可买入,若反向BFS可到达这个点,则说明这个点可卖出
其实就是正向加反向BFS扫两遍,注意判重,要放在更新答案之后,也不用担心后面的答案又会变小什么的,因为即使 u-v,v-u 出现回路,使得答案会改变,若v点买入价比u点低,并且可以到终点(进行交易的充要条件),则我们反向BFS的时候一定可以更新到v点,只要最后确认答案的时候O(n)地扫一边所有的点,就可以找到最优的答案
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
queue <int> q1, q2;
const int MAXM = 500010 * 2;
const int MAXN = 100000 + 10;
const int INF = 1<<30;
inline void rd(int &x) {
x = 0;
char ch = getchar();
while(ch < '0' || ch >'9') ch = getchar();
while(ch >='0' && ch <='9') x = x*10 + ch - '0', ch = getchar();
return;
}
int n,m,last[MAXN],vis[MAXN],ans,sale,buy,tot,value[MAXN],num,revvis[MAXN];
int revlast[MAXN],dis1[MAXN], dis2[MAXN];
struct Edge{
int u,v,to;
Edge(){}
Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[MAXM],rev[MAXM];
inline void add(int u, int v) {
e[++tot] = Edge(u,v,last[u]);
last[u] = tot;
}
inline void addrev(int u, int v) {
rev[++num] = Edge(u,v,revlast[u]);
revlast[u] = num;
}
void bfs1() {
memset(dis1,0x3f,sizeof(dis1));
dis1[1] = value[1];
q1.push(1);
vis[1] = 1;
while(!q1.empty()) {
int x = q1.front();
q1.pop();
for(int i=last[x]; i; i=e[i].to) {
int v = e[i].v;
dis1[v] = min(dis1[x], value[v]);
if(!vis[v]) {
vis[v] = 1;
q1.push(v);
}
}
}
}
void bfs2() {
dis2[n] = value[n];
q2.push(n);
revvis[n] = 1;
while(!q2.empty()) {
int x = q2.front();
q2.pop();
for(int i=revlast[x]; i; i=rev[i].to) {
int v = rev[i].v;
dis2[v] = max(dis2[x], value[v]);
if(!revvis[v]) {
revvis[v] = 1;
q2.push(v);
}
}
}
}
int main() {
rd(n), rd(m);
for(int i=1; i<=n; i++)
rd(value[i]);
for(int i=1; i<=m; i++) {
int x,y,z;
rd(x), rd(y), rd(z);
add(x,y);
addrev(y,x);
if(z == 2)
add(y,x), addrev(x,y);
}
bfs1();
bfs2();
for(int i=1; i<=n; i++)
ans = max(ans, dis2[i] - dis1[i]);
printf("%d\n", ans);
return 0;
}