• 单源最短路径--Dijkstra


    Dijkstra的用途:

    • Dijkstra是一个求单源最短路径的算法。

    • "单源最短路径",顾名思义,从一个源头到其他结点的最短路径。而这个算法,可以求出单个点对其他所有点的最短路径长度。(有些情况是Dijkstra不能处理的,比如负边权,遇到这类情况可能就需要使用其他算法了)

     
    Dijkstra的思想:

    • 主要思想:

    • 从源结点开始,遍历源结点的所有连接的边,并进行排序,再从边的值最小的一个边开始遍历,在每一次遍历的过程中,都尝试更新所储存的某个结点对于源结点的最短路径长度(假设我们要到点B,目前我们遍历到了点A,如果点A到点B的距离加上点A到源点的距离比目前我们之前存储的 源点到点B的距离短的话,就更新源点到点B的最短路径,并在之后遍历连着点B的点(也就是进行BFS))。

    • 例子:

    • 首先,从源结点开始进行BFS(也就是遍历到达的点所向外连着的点.比如A->B B->A A->C(A连B,B连A,A连C),那么BFS A结点就是B C)

    • 在BFS的过程中,每到一个点,就比较一下从 源头到这个点的路径长度 和 之前求的从源头到这个点的最短路径的长度 大小,如果现在这个路径长度比较小,就存起来

    Dijkstra的实现方法:

    • 首先,存图(使用邻接表或链式前向星(如果不会存图建议先学存图,因为这里不会讲怎么存图和BFS)).
    • 算法运行过程:
    1. 把数据(也就是dis数组)初始化为INF(为什么要这样做?这是因为我们要使用较小的值不停更新它,这样最终就是源点到这个点的最小值了。那怎么知道某个点所对应的与源点的距离呢?一般是把源点的序号当做数组的下标。下标是什么意思呢?比如使用这个代码:int i=A[10],10就是数组A的一个下标。源点的序号是什么意思呢?一般使用源点读入的顺序当做源点的序号)
    2. 准备一个优先队列,用于之后进行的BFS.
    3. 在BFS的过程中不断更新最小值,并且,如果一个点被更新了,就再把被更新的点推入优先队列,准备之后在这个点上进行BFS.
    4. 优化

    Dijkstra的优化:

    • 优化方法:
    • 堆优化(这就是使用优先队列(这里我的代码使用了STL中的priority_queue)的原因)。因为我们是从源点开始遍历,并不停用离源点尽量小的点来更新其他点的当前最短路径,所以如果遍历队列中的结点时,发现这个结点中所存储的 向队列推入这个点时,这个点离源点的距离 与当前这个点到源点的最短路径不同的话,这是因为这个点的最短路径已经被别人更新过了,也就是说这个点的数据已经无法使用了。并且,在更新这个点的最短路径(在我的实现中使用了dis数组(这个括号内的内容并不重要,只是为了防止一些人不明白怎么更新这个点的最短路径))时,肯定已经把这个点推到队列中了。因此可以得出:如果这个点中储存的最短路径(我们用Node结构体,存储点的序号和将这个点推入队列时源点到这个点的最短路径)和目前的最短路径不一样,那么必然这个点中存储的最短路径是较长的,并且这个点已经无用了,只会造成重复计算 所以,当我们判断到这个条件时,就可以直接跳过这个结点。

    Dijkstra的使用注意事项:

    • 如果图是无向图,需要双向加边(也就是调用两次addEdge方法,比如A-B这条边 就可以addEdge(a,b);addEdge(b,a)(这里忽略了权值))

    模板题目地址:https://www.luogu.org/problemnew/show/P4779

    重载运算符版:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int s,cnt=0,dis[1000010],head[1000010];
    //s:源结点 cnt:边总数,用于存边 dis:存储当前的源结点到某个结点的最短路径长度(下标是结点序号) head:邻接表, 
    struct Edge{
        int v,w,nxt;
    }e[500010];
    struct Node{
        int u,d;
        bool operator <(const Node& rhs)const{
            return d>rhs.d;//重载运算符,这个需要背过 
        }
    };
    void addEdge(int u,int v,int w){
        e[++cnt].v=v;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        //关于邻接表: 
        //首先要明确,邻接表是一个链式结构,也就是说,首先添加的边在邻接表的最后面
        //最后添加的在前面,在遍历的时候也是从头往后遍历,也就是说最先添加的反而最后被遍历到. 
        //当遍历到最后一个的时候 由于最后一个的后面已经没有结点了,那么此时i的值就成为了0(具体看下面的for循环代码)
        //成为0了,也就代表着循环该结束了.这时候,循环条件不成立,循环就结束了. 
        head[u]=cnt;
    }
    void dijkstra(){
        dis[s]=0;
        priority_queue<Node>q;
        q.push((Node){s,0});
        while(!q.empty()){
            Node now=q.top();q.pop();
            int d=now.d,u=now.u;
            if(d!=dis[u])continue;//堆优化,等同于 if(d>dis[u])continue;,可以相对较好地减少时间复杂度
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].v,w=e[i].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    q.push((Node){v,dis[v]});
                }
            }
        }
    }
    int main(){
        int n,m;
        scanf("%d%d%d",&n,&m,&s);
        for(int i=1;i<=n;i++)dis[i]=2147483647;//将源结点到其他结点的路径长度初始化为尽可能大的值,也就是INF 
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            addEdge(x,y,z);
        }
        dijkstra();
        for(int i=1;i<=n;i++)printf("%d ",dis[i]);
        return 0;
    }

    Pair版:

    #include<bits/stdc++.h>
    #include<iostream>
    #define P pair<long long,int>
    #define INF 2147483647
    #define maxn 500005
    using namespace std;
    priority_queue<P,vector<P>,greater<P> >q;
    int dis[maxn],vis[maxn],head[maxn];
    int n,m,s;
    int cnt;
    struct Edge{
        int u,v,w,next;
    }e[maxn*2];
    void add(int u,int v ,int w){
        e[++cnt].u=u;
        e[cnt].v=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
    } 
    void dijkstra(){
        while(!q.empty()){
            int x=q.top().second;
            q.pop();
            if(!vis[x]){
                vis[x]=1;
                for(int i=head[x];i;i=e[i].next){
                    int v=e[i].v;
                    dis[v]=min(dis[v],dis[x]+e[i].w);
                    q.push(make_pair(dis[v],v));
                }
            }
            
        }
        
    }
    int main(){
        cin>>n>>m>>s;
        for(int i=1;i<=m;i++){
            dis[i]=INF;
        }
        dis[s]=0;
        q.push(make_pair(0,s));
        for(int i=1;i<=m;i++){
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c); 
        }
        dijkstra();
        for(int i=1;i<=n;i++){
            cout<<dis[i]<<" ";
        }
        cout<<endl;
        return 0;
            
    }

     

  • 相关阅读:
    文本框设置只读,后台可获取
    div 在同一行的 CSS处理
    在标签中添加属性
    (转)如何使用SignalTap II觀察reg與wire值? (SOC) (Verilog) (Quartus II) (SignalTap II)
    (转)如何使用ModelSim對Megafunction或LPM作仿真? (SOC) (MegaCore) (ModelSim)
    (笔记)TSL235新型光感器件强烈推荐使用
    (转)如何增加SignalTap II能觀察的reg與wire數量? (SOC) (Quartus II) (SignalTap II)
    (转) 如何將10進位轉2進位? (C/C++) (C)
    (转)如何使用ModelSim作前仿真與後仿真? (SOC) (Quartus II) (ModelSim)
    (笔记)关于LM3S片内FLASH编程的一点建议
  • 原文地址:https://www.cnblogs.com/zbsy-wwx/p/11680721.html
Copyright © 2020-2023  润新知