前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >从算法题理解Spfa算法与Dijsktra算法本质区别

从算法题理解Spfa算法与Dijsktra算法本质区别

原创
作者头像
潋湄
修改2024-12-11 09:37:20
修改2024-12-11 09:37:20
8700
代码可运行
举报
运行总次数:0
代码可运行

今日推荐文章:https://cloud.tencent.com/developer/article/2473454?policyId=20240001&traceId=01jesnm005djmn2rgw9gnazq7b 在用户量庞大的应用系统中,如何进行系统的云原生架构是一件很有挑战的事情,本文以实践案例介绍了宝贵的架构经验,值得好好学习

这道题是一道隐藏的最短路问题,但是它又蕴含着很多细节值得我们考虑,看了很多人的博客,都只是浅浅地说可以用spfa算法,但是求单源最短路问题还有Dijsktra算法,为什么这道题不能用Dijsktra算法呢,下面是本人的一些理解。

首先看题目: https://www.luogu.com.cn/problem/P1073 最优贸易 - 洛谷

最优贸易
最优贸易

       题意很简单,就是阿龙从1号城市出发,中间可以经过任何一个城市,且一个城市可以经过两次,完了可以进行一次买入与一次卖出交易,求最后获取交易的最大值。

       完了我们思考,假设阿龙在 i 城市买入水晶球,在城市 j 卖出水晶球,那么也就是说,阿龙从起点走到城市 i 的路程上买水晶球的最少价格就是price i ,从城市 j 走到终点的路程上卖出水晶球的最多价格就是price j ,也就是说,我们可以把整个路程划分为下面三个部分:

买入价格与卖出价格对比分析
买入价格与卖出价格对比分析

       这样看很直观得把整段路程分为了三部分,那么现在的核心就是如何求出每一段上的每个点对应的买入最小值,卖出最大值。

        我们发现一段路程上,买入最小值永远都是所有已经经过点的最小值,卖出最大值永远都是未来经过点的最大值,于是我们想到了最短路求解,而我们要分别求最小值与最大值,于是我们可以求两边最短路,分别求正向的买入最小值minn i ,反向的卖出最大值maxn i ,这样枚举每一个点的差值,最大的差值就是最大交易收入

        下面我们就要选择最短路算法,可能很多人想到了Dijsktra算法(我一开始也这样,完了WA了),但是,这道题卡掉了Dijsktra算法,为什么呢?

       因为Dijsktra算法保证每个已经更新了最短距离的点后面距离就不会更新,但是在这道题中,假使你走到城市 i ,后面可能还会有买入价格更低的城市,这样必须还要更新,所以Dijsktra算法行不通,于是我们可以用SPFA算法,每次都将出队的点设置为未遍历,这样后面还会继续更新。

清楚了这两点之后,代码的书写就水到渠成了,下面附上源代码:

代码语言:cpp
代码运行次数:0
复制
#include<iostream>
using namespace std;
#include<cstring>
#include<queue>
 
typedef pair<int,int> PII;
 
const int N = 1e5+10, M = 2e6+10, INF = 0x3f3f3f3f;
int hs[N],e[M],ne[M],w[M],idx;
int price[N],ht[N],n,m,dmax[N],dmin[N];
bool st[N];
 
void add(int h[],int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
 
//flag为真表示求正向走路买入的最小值
void SPFA(int u,int h[],int dist[],bool flag){
    memset(st,0,sizeof(st));
    queue<int>q;
    if(flag) memset(dist,0x3f,sizeof(dmax));
    dist[u]=price[u];
    q.push(u);
    st[u]=true;
    
    while(q.size()){
        int t=q.front();
        q.pop();
        //每次出队后设置为未访问,保证后面距离能继续更新
        st[t]=false;
        for(int i=h[t];~i;i=ne[i]){
            int j=e[i];
            if((flag&&dist[j]>min(price[j],dist[t]))||(!flag&&dist[j]<max(dist[t],price[j]))) {
                if(flag) dist[j]=min(price[j],dist[t]);
                else dist[j]=max(dist[t],price[j]);
                if(!st[j]){
                    st[j]=true;
                    q.push(j);
                }
            }
        }
    }
}
 
int main(){
    cin>>n>>m;
    
    for(int i=1;i<=n;i++) scanf("%d",&price[i]);
    
    memset(hs,-1,sizeof(hs));
    memset(ht,-1,sizeof(ht));
    
    int a,b,c;
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&a,&b,&c);
//建立反向图
        add(hs,a,b),add(ht,b,a);
        if(c==2) add(hs,b,a),add(ht,a,b);
    }
    
//这里求最小值从1开始,求最大值从n开始
//因为结合路程图,我们是在1~i城市买入水晶球,在i~n城市卖出水晶球
//所以要从1开始求到达i之前买入水晶球最少价格
//从n开始求到达i之前能够卖出的最大价格,之后做差值
    SPFA(1,hs,dmin,true);
    SPFA(n,ht,dmax,false);
    
    int res=0;
    for(int i=1;i<=n;i++) res=max(res,dmax[i]-dmin[i]);
    
    cout<<res<<endl;
    
    return 0;
}

 这就是这道题的全部内容了,值得思考的点还是很多的,希望各位有所收获!!!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档