191114-最优贸易
简化题意
本题是让我们在一张带节点权值的图上,找出一条从1到n的路径,使路径上能选出两个p,q(先经过点p,再经过点q),并且 a p − a q a_p-a_q ap−aq的值最大(a数组表示节点的权值)
解析
首先我们给这张图建一张反图,第一次在原图上跑spfa,求出节点1到各个节点所经过路径上最小点权值(记为 d i d_i di),然后在反图上跑一遍spfa,求出从节点n到各个节点所经过路径上最大点权值(记为 f i f_i fi),最后枚举每个点,计算 f i − d i f_i-d_i fi−di的值,然后更新答案
题解
#include<bits/stdc++.h>
#define M 200008
using namespace std;
int nxt1[M*2],nxt2[M*2],first1[M],first2[M],to1[M*2],to2[M*2],d[M],f[M],n,m,ans,a[M],tot;
bool vis[M];
int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
void add1(int x,int y){
nxt1[++tot]=first1[x];
first1[x]=tot;
to1[tot]=y;
}
void add2(int x,int y){
nxt2[++tot]=first2[x];
first2[x]=tot;
to2[tot]=y;
}
void spfa_1(){
queue<int>q;
memset(d,127,sizeof(d));
d[1]=a[1];
q.push(1);
vis[1]=1;
while(q.size()){
int v=q.front();
q.pop();
vis[v]=0;
for(int i=first1[v];i;i=nxt1[i]){
int u=to1[i];
if(d[u]>min(d[v],a[u])){
d[u]=min(d[v],a[u]);
if(!vis[u]){
q.push(u);
vis[u]=1;
}
}
}
}
}
void spfa_2(){
queue<int>q;
memset(vis,0,sizeof(vis));
f[n]=a[n];
q.push(n);
vis[n]=1;
while(q.size()){
int v=q.front();
q.pop();
vis[v]=0;
for(int i=first2[v];i;i=nxt2[i]){
int u=to2[i];
if(f[u]<max(f[v],a[u])){
f[u]=max(f[v],a[u]);
if(!vis[u]) {
q.push(u);
vis[u]=1;
}
}
}
}
}
int main(){
int x,y,z;
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int j=1;j<=m;j++){
x=read(),y=read(),z=read();
if(z==1){
add1(x,y);
add2(y,x);
}
else{
add1(y,x);
add1(x,y);
add2(x,y);
add2(y,x);
}
}
spfa_1();
spfa_2();
for(int i=1;i<=n;i++)
ans=max(f[i]-d[i],ans);
printf("%d",ans);
return 0;
}
题外话
该题也可以用双向bfs做