题解 P1073 【最优贸易】
2018.11.3更新
经xunzhen大佬指出错误,发现在更新过程中省略了一个关键步骤:flag[y]=0,目前已更正。欢迎各位大佬指出错误,一起探讨,进步
2019.9.30更新
加入latex,优化观感(虽然没人看)
这道题是我们刚学最短路和差分约束系统的考试题,所以想的时候直接就奔着这个方向去了。下面是本蒟蒻的思路(第一次写题解,排版太丑,还请各位神犇见谅)
1. 预处理
要跑最短路(这道题实际是最长路),首先我们要建一个图。而这道题没有边权,那么就需要我们自己来建边。因为每一个城市的水晶球有自己的价格,所以如果是从低价走到高价,我们可以赚钱,那么就建一条正边,而如果不能赚钱,那就建一条为0的边。
void addedge(int x,int y,int z){
e[++cnt].next=h[x];
e[cnt].to=y;
e[cnt].value=z;
h[x]=cnt;
}//前向星标准写法
for(int i=1;i<=n;i++) v[i]=read();
for(int i=1;i<=m;i++){
int a,b,c;
a=read();
b=read();
c=read();
addedge(a,b,max(0,v[b]-v[a]));//可以赚v[b]-v[a]的钱或者赚不了球
if(c==2) addedge(b,a,max(0,v[a]-v[b]));//同上
}
2. 核心算法(spfa)
现在图已经建好了,现在应该跑一遍最短路最长路了。而此题需要注意的是只能进行一次贸易,这也是和普通的最长路不同的地方,所以我在这里多开了一个最短路最长路的写法(
3. 完美收尾
到了现在,图已经建好了,spfa也写好了,拼起来最后
#include<cstdio>
#include<iostream>
#include<queue>
#define inf 0x7fffffff/2
using namespace std;
inline int read(){
char c;
int x=0,flag=1;
for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') flag=-1;
for(;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
return x*flag;
}//快读
struct edge{
int to,next,value;
};
edge e[1000005];
int n,m,v[100005],cnt,h[100005],vis[100005],flag[100005],d[100005];
void addedge(int x,int y,int z){
e[++cnt].next=h[x];
e[cnt].to=y;
e[cnt].value=z;
h[x]=cnt;
}
queue<int> q;
void Spfa(){
for(int i=1;i<=n;i++) d[i]=-inf;
d[1]=0;
q.push(1);
vis[1]=1;
while(!q.empty()){
int k=q.front();
vis[k]=0;
for(int i=h[k];i;i=e[i].next){
int y=e[i].to;
if(!flag[k]){
if(d[y]<d[k]+e[i].value){
d[y]=d[k]+e[i].value;
if(!vis[y]){vis[y]=1;q.push(y);}
if(v[y]<v[k]) flag[y]=1;
}
}
else{
if(d[y]<d[k]){
d[y]=d[k];
if(!vis[y]){vis[y]=1;q.push(y);}
flag[y]=1;//继承前面的贸易,可以理解为水晶球在前面已经卖掉,没有传到这里
}
if(d[y]<e[i].value){
d[y]=e[i].value;//开始新的贸易
flag[y]=0;
if(!vis[y]){vis[y]=1;q.push(y);}
}
}
}
q.pop();
}
}
int main(){
n=read();
m=read();
for(int i=1;i<=n;i++) v[i]=read();
for(int i=1;i<=m;i++){
int a,b,c;
a=read();
b=read();
c=read();
addedge(a,b,max(0,v[b]-v[a]));
if(c==2) addedge(b,a,max(0,v[a]-v[b]));
}
Spfa();
cout<<d[n];
}