题意分析
反向建图的思想应该是很常见的。
一开始一直在想直接做一次SPFA求出结果,发现一直无法维护找到的最大最小值和能够更新的先后关系。后来看了一下题解才明白怎么回事。
首先正向建图,因为一开始是没有购买水晶球的,首先应该找一个地方购买水晶球,按照贪心的思想,希望购买到的水晶球的价钱尽量低。这时候做SPFA,意义为找到从起点开始能到达的点,其能买到水晶球最便宜的价格为多少。
SPFA的过程也不难,松弛的条件为,如果下一个节点的最小值比当前点最小值小,即做松弛。 注意松弛的时候应该将下一个节点的值更新为 min(当前节点的值,下一个节点的值),才能保证下一个节点的值为最小。
然后反向见图。 反向见图的含义是,如果我买到了水晶球,从哪卖出去能够获得最大收益。还是按照贪心的思想,希望卖出的水晶球的价格越高越好,SPFA过程维护的是,从终点开始能够到达的点,其能卖出水晶球的最贵的价格为多少。
SPFA的过程松弛条件与刚才相反,遇到更大的就松弛。
结果为所有点对应的卖出与买入的差值最大值,遍历求出即可。
代码总览
#include<bits/stdc++.h>
using namespace std;
const int nmax = 500005;
const int mmax = 100005;
typedef struct{
int to,nxt;
} Edge;
Edge e[nmax<<1], e2[nmax<<1];
int a[mmax],head[nmax<<1],head2[nmax<<1];
int p[mmax],p2[mmax];
int n,m,tot = 0,tot2 = 0;
void add(int u,int v){
e[tot].to = v;
e[tot].nxt = head[u];
head[u] = tot++;
}
void add2(int u,int v){
e2[tot2].to = v;
e2[tot2].nxt = head2[u];
head2[u] = tot2++;
}
void spfa(){
memset(p,0x3f3f3f3f,sizeof p); p[1] = a[1];
bitset<mmax> inque; inque.reset();
queue<int> q; q.push(1);
while(!q.empty()){
int u = q.front(); q.pop(); inque.reset(u);
for(int i = head[u]; i!=-1; i=e[i].nxt){
int v = e[i].to;
if(p[v] > p[u]){
p[v] = min(p[u],a[v]);
if(!inque.test(v)){
q.push(v);
inque.set(v);
}
}
}
}
}
void spfa2(){
memset(p2,-1,sizeof p2); p2[n] = a[n];
bitset<mmax> inque; inque.reset();
queue<int> q; q.push(n);
while(!q.empty()){
int u = q.front(); q.pop(); inque.reset(u);
for(int i = head2[u]; i!=-1; i=e2[i].nxt){
int v = e2[i].to;
if(p2[v] < p2[u]){
p2[v] = max(p2[u],a[v]);
if(!inque.test(v)) {
q.push(v);
inque.set(v);
}
}
}
}
}
int main(){
memset(head,-1,sizeof head);
memset(head2,-1,sizeof head2);
scanf("%d %d",&n,&m);
for(int i = 1;i<=n;++i) scanf("%d",&a[i]);
int u,v,w;
for(int i = 1;i<=m;++i) {
scanf("%d %d %d",&u,&v,&w);
if(w == 1){add(u,v) ; add2(v,u);}
else {add(u,v);add(v,u);add2(u,v);add2(v,u);}
}
spfa();
spfa2();
int ans = 0;
for(int i = 1;i<=n;++i)
if(p[i]!=0x3f3f3f3f && p2[i]!=-1 && p2[i] - p[i] > ans) ans = p2[i] - p[i];
printf("%d\n",ans);
return 0;
}