Dijkstra算法_最短路算法

\(Dij\)是一类基于贪心的最短路算法。
首先我们明确这个算法是干什么的,这个算法是用来跑单源最短路的,也就是给你一张图,给出某一个特定的起点,你需要求出这个点到其他任意点的最短路。

其次,我们明确它可以解决什么样的问题,它可以解决单源最短路问题,实际上,对于给一个起点问到其他点最短路的问题,它是未必能够解决的。
它是用于跑单源最短路的一个算法,不过,算法的正确性是基于只存在非负边权,如果存在负数边权,那么它就无法保证正确性了。

上面已经知道了它的用途,那么接下来我们讲一讲如何实现。

初始化

首先我们初始化,记录一个\(dis[]\)数组,\(dis[i]\)表示给定的起点\(s\)\(i\)的最短距离,那么一开始都设成正无穷,意思是这些点目前都无法到达,然后,我们把\(dis[s]\)设为\(0\),含义是自己到自己是\(0\),然后我们把\(s\)这个点放入更新集合,到这里,我们就完成了\(Dij\)算法的初始化。\(dis[]\)数组,也正是我们要求出来的答案数组。
Code

memset(dis,0x3f,sizeof(dis));//初始化为正无穷
priority_queue <node> Q;//开一个更新集合,为啥用优先队列会在下文讲解
Q.push((node){0,s});//把s放进去 在更新集合里面的点可以往外拓展,进行更新
dis[s]=0;//初始化自身dis

算法过程

从更新集合中取出\(dis\)最小的,然后让他扩展,扫所有能到达的点\(to\),如果\(dis[to]>dis[更新点]+边权\),那么我们就把\(dis[to]\)塞到更新集合里,并更新它的\(dis\)

用优先队列,就是为了优化取出这个\(dis\)最小的位置,为什么不把没有更新的塞到集合里?因为如果此时它不能被更新,是不是说明这个点的\(dis\)一定曾经被更新过,不再是正无穷了?那么因为他被更新过,它是不是被放进过更新集合?也就是它被更新过的那个\(dis\),一定曾经在集合里,那么就不用放进去了,否则就重复了。

正确性?

为什么我们\(Dij\)取出来之后就打标记不再取了?这是因为我们知道,必然接下来取出来的是曾经被放进去的这个点,必然是那些不优解,什么?你觉得可能有更优解?不可能的,注意到如果所有的边都是正权边,我们每一次放进去的点的\(dis\)都是单调递增的,从而不可能一个点被取出来之后重新放进去更优,如果在他之前放进去,那肯定比它还先取出来,从而我们证明了,取出来一遍就可以打标记永远不取了。
那么怎么保证一定会取到最优解?这是因为我们应用了贪心的思想,每次都用最优点更新,但凡有更优解,一定会在某一个时刻优于这个不优的状态,从而被先取出来。

第一个正确性保证了时间复杂度,第二个正确性保证了算法的正确性。

\(summary\)

人生中贪心不一定是好事,但算法中的贪心,更多的是朝着更优的情况努力的贪心,是一种拼搏进取向上的贪心,该为更好自己"贪心"的时候就应该贪心。

\(Dij\)的思想也是贪心,但人生没有最短路,每一条道路,都是最完美的安排,每一个人,都是最完美的人。

完整\(Code\)

#include<bits/stdc++.h>
using namespace std;
const int N=100086,M=500086;
int n,m;
int cnt=-1,dis[N],vis[N],s;
int head[N];
struct Edge{
    int nxt,to,v;
}e[M];
struct Node{
    int dis,id;
    bool operator < (const Node &t)const{
        return dis>t.dis;///堆默认大根,所以大于号定义小于号则有小根堆
    }
};
priority_queue<Node> Q;
void add_edge(int x,int y,int v){
    e[++cnt]=(Edge){head[x],y,v};///cnt初始值-1,边0--(m-1) 下一边为原链表头,记录到谁,边权
    head[x]=cnt;///覆盖原链表头,成为头
}
void Dij(){
    memset(dis,0x3f,sizeof(dis));///初始化
    memset(vis,0,sizeof(vis));///初始化
    Q.push((Node){0,s});///初始化
    dis[s]=0;///初始化
    for(;!Q.empty();){
        Node t=Q.top();
        Q.pop();
        int id=t.id;
        if(vis[id])continue;
        vis[id]=1;
        for(int p=head[id];~p;p=e[p].nxt){
            int y=e[p].to,v=e[p].v;
            if(dis[y]>dis[id]+v){
                dis[y]=dis[id]+v;
                Q.push((Node){dis[y],y});
            }
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&s);///点数 边数 起点
    memset(head,-1,sizeof(head));///链表初始-1
    for(int i=1,x,y,v;i<=m;i++){
        scanf("%d%d%d",&x,&y,&v);
        add_edge(x,y,v);
        ///add_edge(y,x,v);   双向边
    }
    Dij();
    for(int i=1;i<=n;i++){
        if(dis[i]==0x3f3f3f3f){
            printf("NO PATH\n");
        }
        else printf("%d\n",dis[i]);
    }
    return 0;
}

posted @ 2020-12-22 21:11  Z_char  阅读(178)  评论(0编辑  收藏  举报