题解 | #[NOIP2009]最优贸易#
[NOIP2009]最优贸易
https://ac.nowcoder.com/acm/problem/16611
题意
在 从 1号点到 n号点 的路径中, 选择两个城市 A 和 B,必须先选 A 再选 B,问 能赚取的差价最大是多少
A:买入水晶球
B:卖出水晶球
ps:1 号点到 n 号点的路径可能不止一条 .
思路
不妨遍历 n 个点,对于每个点求出 res_i = w2[i] - w1[i]
w1[i]:买入。在从 1 到 i 的路径中,水晶球的最小价格 (若从 1 到不了 i ,则 w1[i] = 正无穷 )
w2[i]:卖出。在从 n 到 i 的路径中,水晶球的最大价格 (若从 n 到不了 i ,则 w2[i] = 0 ) // 要求我们建立反向边
因此最后的 ans = max( 0, res_1, res_2, ...... , res_n )
求 w1[ ] :正向 spfa
求 w2[ ] :反向 spfa ,要求我们建立反向边,比如 a ==> b 的边,我们就建立 b ==> a 的边
ps:此处不可以使用 dijkstra ,因为 dijkstra 是从起点开始依次选择距离起点最短的边,从而形成的最短路,而此题 一方面 不是让求最短路,一方面 没有边权.
综上,Code 如下.
Code
#include <bits/stdc++.h> using namespace std; const int N=100010,M=500010; int a[N]; bool st1[N],st2[N]; int h1[N],e1[M],ne1[M],idx1; int h2[N],e2[M],ne2[M],idx2; int w1[N],w2[N]; int n,m; void add1(int a,int b){ // 建立正向边 e1[idx1]=b,ne1[idx1]=h1[a],h1[a]=idx1++; } void add2(int a,int b){ // 建立反向边 e2[idx2]=b,ne2[idx2]=h2[a],h2[a]=idx2++; } void spfa1(){ // 从起点开始 memset(w1,0x3f,sizeof w1); st1[1]=true; queue<int>q; q.push(1); w1[1]=a[1]; while(q.size()){ int t=q.front(); q.pop(); st1[t]=false; for(int i=h1[t];~i;i=ne1[i]){ int j=e1[i]; if(a[j]<w1[j]||w1[t]<w1[j]){ // 存在回路,即可能 w1[j] 比两者都小(或等于),此时不需要更新 w1[j]=min(w1[t],a[j]); // j 点被更新 if(!st1[j]) st1[j]=true,q.push(j); // 被更新的点 其 后面的点可能也需要更新 所以 j 入列 } } } } void spfa2(){ //从终点开始 st2[n]=true; queue<int>q; q.push(n); w2[n]=a[n]; while(q.size()){ int t=q.front(); q.pop(); st2[t]=false; for(int i=h2[t];~i;i=ne2[i]){ int j=e2[i]; if(a[j]>w2[j]||w2[t]>w2[j]){ // 原因同上 w2[j]=max(a[j],w2[t]); if(!st2[j]) st2[j]=true,q.push(j); } } } } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; memset(h1,-1,sizeof h1); memset(h2,-1,sizeof h2); while(m--){ int x,y,z; cin>>x>>y>>z; if(z==1) add1(x,y),add2(y,x); //同时建立反向边 else add1(x,y),add1(y,x),add2(x,y),add2(y,x); //同时建立反向边 } spfa1(); spfa2(); int res=0; for(int i=1;i<=n;i++) res=max(res,w2[i]-w1[i]); cout<<res<<endl; return 0; }