最短路大致有两个模板,此题是用的SPFA.
题目:最优贸易
做法:
此题做法两点启发:
1、用 DP 的思想考虑问题。
因为此题只有一买一卖,所以按照买卖的分界点来进行划分,即假设分界点为 k k k ,其意义为买水晶球在 1 1 1~ k k k 点进行,买入的最小值记为 d m i n [ k ] dmin[k] dmin[k] ,同样,卖水晶球在 k k k~ n n n 点进行,卖出的最大值记为 d m a x [ k ] dmax[k] dmax[k] .
接下来考虑状态转移,设 k k k 可达到的点集为 { u } \{u\} {u},则转移方程为 d m i n [ k ] = m i n ( d m i n [ u i ] ) dmin[k]=min(dmin[u_i]) dmin[k]=min(dmin[ui]),最大值转移同理。
2、图论实践。
首先因为此题有环,所以不能用 DP 直接解题,所以考虑最短路解法。
又因为此题**不满足 D i j k s t r a Dijkstra Dijkstra 最短路算法的贪心性质(值不是累加的)**所以采用SPFA.
此题同时要知道两组最值路径,并且一部分是 1 1 1~ k k k的性质,另一部分是 k k k~ n n n的性质,所以前半部分是顺序存图来保证性质正确,后半部分要反向存图来保证性质正确,所以用两个邻接表同时用两边 SPFA 即可。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N=100010,M=2000010;
int idx,ver[M],h[N],hr[N],ne[M];
int n,m;
int w[N];
int dmin[N],dmax[N];
bool v[N];
void add(int h[],int x,int y){
ver[++idx]=y;
ne[idx]=h[x];
h[x]=idx;
}
void spfa_1(){
queue<int> q;
memset(dmin,0x3f,sizeof(dmin));
memset(v,0,sizeof(v));
q.push(1);
dmin[1]=w[1];
v[1]=true;
while(q.size()){
int t=q.front();
q.pop();
v[t]=false;
//cout<<t<<endl;
for(int i=h[t];i;i=ne[i]){
int j=ver[i];
if(dmin[j]>min(dmin[t],w[j])){
dmin[j]=min(dmin[t],w[j]);
if(!v[j]){
v[j]=true;
q.push(j);
}
}
}
}
}
void spfa_2(){
queue<int> q;
memset(dmax,-0x3f,sizeof(dmax));
memset(v,false,sizeof(v));
q.push(n);
dmax[n]=w[n];
v[n]=true;
while(q.size()){
int t=q.front();
q.pop();
v[t]=false;
for(int i=hr[t];i;i=ne[i]){
int j=ver[i];
if(dmax[j]<max(dmax[t],w[j])){
dmax[j]=max(dmax[t],w[j]);
if(!v[j]){
v[j]=true;
q.push(j);
}
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(h,a,b);
add(hr,b,a);
if(c==2){
add(h,b,a);
add(hr,a,b);
}
}
spfa_1();
spfa_2();
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dmax[i]-dmin[i]);
cout<<ans<<endl;
return 0;
}