zoukankan      html  css  js  c++  java
  • 最短路---Dijkstra学习笔记

    最短路---Dijkstra

    最近蒟蒻的自己重新学习了一遍最短路,也算有些体会,记录下来。

    首先引入问题:在一张图中,从某一顶点出发,沿图的边到达定一个顶点所经过的路径中,各边权值和最小的一条路径。

    解决该问题的算法有:Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法

    Dijkstra算法:

    介绍:Dijkstra算法是单源最短路算法的一种,用于求出发节点到所有可达节点的最短路长度。

    限制:路径权值必须为非负数  无负权回路  单源最短路

    算法思想:Dijkstra算法运用了贪心的思想,通过不断寻找最短距离的点,用以该点为弧尾的点的边更新其他的路径(松弛)。设起点为start,终点为endstart-->end的最短路径只有两种情况。

     1、start--->end   (从起点到终点直接为最短路径)

     2、Start--->v1--->v2...--->end (从起点通过其他的点到达终点的路径)

    算法步骤:

     把顶点V分成两组:

      S:已经求出最短路径的顶点集合

     T=V-S:尚未确定最短路径的顶点集合

     1、初始时:令S={V0}  T={其余顶点}  T中的顶点对应的距离值若存在<V0,Vi>,则为该边的权值,若不存在则为INF(正无穷) 

     2、T中选取一个距离最小的顶点W,将该点加入集合S中。并用该点对T中顶点的距离进行修改:若加入w作为中间顶点(V0-->W-->Vn,该路径的距离比不加入W的路径更短,则修改此距离值。

    3、重复上述步骤,知道S中包含所有顶点,即S=V为止

     算法实现过程中需要两个数组 

    1、dist数组记录源点到每个点的最短路径大小

    2、vis数组记录该点是否已经在集合S中(即是否已经找到到该点的最短路径)

    算法过程(手动模拟,QAQ丑字不要介意)

     

     代码实现

    这里用一道最短路的模板题来展示该算法的代码。(POJ - 2387 Til the Cows Come Home)

    题意:给你n个点,给出a到b的距离,a,b边是可以互想抵达的,求1到n的最短距离。实质:n个顶点m条边的无向图,求1到n的最短路径。

    //邻接矩阵存图版本
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int MAX=1005;
    const int INF=0x3f3f3f3f; 
    int t,n;                //边数 点数 
    int map[MAX][MAX];        //邻接矩阵存图 
    int dist[MAX];            //dist数组存源点到各个点的最短路径 
    bool vis[MAX];            //标记是否找到最小值(是否在集合S中)
    void Dij(int start)
    {
        for(int i=0;i<=n;i++)        //初始化 dist全为正无穷,vis全为false 
        {
            dist[i]=INF;
            vis[i]=false;
        }
        dist[start]=0;                //源点到源点的值为0
        int min=INF,pos;            //寻找dist数组中最小值,并记录其所在的点 
        for(int i=1;i<=n;i++)
        {
            min=INF;
            for(int j=1;j<=n;j++)        //寻找dist中的最小值 
            {
                if(!vis[j]&&dist[j]<min)
                {
                    min=dist[j];
                    pos=j;
                }
            }    
            vis[pos]=true;                    //将该点加入集合S中 
            for(int j=1;j<=n;j++)            //用该点为弧尾的边进行松弛操作 
            {
                if(!vis[j]&&dist[j]>dist[pos]+map[pos][j])
                    dist[j]=dist[pos]+map[pos][j];
             } 
        } 
    
    } 
    int main()
    {
        while(scanf("%d%d",&t,&n)!=EOF)
        {
            for(int i=1;i<=n;i++)                //初始化 
                for(int j=1;j<=n;j++)
                    map[i][j]=INF;
            for(int j=0;j<t;j++)
            {
                int a,b,len;
                scanf("%d%d%d",&a,&b,&len);
                if(map[a][b]>len)                //去除重边(如果先输入的1 2 10则为点1-->2的距离为10,若在输入1 2 100则为
                    map[a][b]=map[b][a]=len;    //点1-->2的距离为100 但是第二次输入会覆盖第一次输入,该更短的边不见,影响结果) 
            }
            Dij(1);                                //以1为源点进行Dijkstra 
            printf("%d
    ",dist[n]);
        }
        return 0;
    } 
    //邻接表存图
    #include<iostream> 
    #include<cstdio>
    #include<vector>
    #include<cstring>
    using namespace std;
    const int MAX=1005;
    const int INF=0x3f3f3f3f;
    int t,n,a,b,len;
    int dist[MAX];
    bool vis[MAX];
    struct point
    {
        int to,val;
    };
    vector<point>e[MAX];
    void Dij(int start)
    {
        for(int i=1;i<=n;i++)                    //初始化 
        {
            dist[i]=INF;
            vis[i]=false;
        }        
        dist[start]=0;                            //源点到源点距离为0 
        int min,pos;
        for(int i=1;i<=n;i++)
        {
            min=INF;
            for(int j=1;j<=n;j++)                //找dist数组的最小值 
            {
                if(!vis[j]&&dist[j]<min)
                {
                    min=dist[j];
                    pos=j;
                }
            }
            vis[pos]=true;
            for(int j=0;j<e[pos].size();j++)    //用找到的点为弧尾的边进行松弛 
            {
                int to=e[pos][j].to,val=e[pos][j].val;
                if(!vis[to]&&dist[to]>dist[pos]+val)
                    dist[to]=dist[pos]+val;
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&t,&n)!=EOF)
        {
            point temp;
            for(int i=0;i<t;i++)
            {
                scanf("%d%d%d",&a,&b,&len);                //邻接矩阵存图 
                    temp.to=a,temp.val=len;
                    e[b].push_back(temp);
                    temp.to=b;temp.val=len;
                    e[a].push_back(temp);
            }
            Dij(1);
            printf("%d
    ",dist[n]);
        }
        return 0;
    } 
    //链式前向星存图
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int MAXN=4009;
    const int MAX=1009; 
    const int INF=0x3f3f3f3f;
    int head[MAX],cnt=0;
    int t,n,a,b,len;
    int dist[MAX];
    bool vis[MAX];
    struct Edge{
        int next,to,val;
    }Edge[MAXN];
    inline void add(int u,int v,int w)
    {
        Edge[cnt].to=v;
        Edge[cnt].val=w;
        Edge[cnt].next=head[u];
        head[u]=cnt++;
    }
    void Dij(int start)
    {
        for(int i=0;i<=n;i++)
        {
            dist[i]=INF;
            vis[i]=false;
        }
        dist[start]=0;
        int min,pos;
        for(int i=1;i<=n;i++)
        {
            min=INF;
            for(int j=1;j<=n;j++)
            {
                if(!vis[j]&&dist[j]<min)
                {
                    min=dist[j];
                    pos=j;
                }
            }
            vis[pos]=true;
            
            for(int i=head[pos];i!=-1;i=Edge[i].next)
            {
                int to=Edge[i].to;
                if(!vis[to]&&dist[to]>dist[pos]+Edge[i].val)
                    dist[to]=dist[pos]+Edge[i].val;
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&t,&n)!=EOF)
        {
            memset(head,-1,sizeof(head));
            for(int i=0;i<t;i++)
            {
                scanf("%d%d%d",&a,&b,&len);                 
                    add(a,b,len);
                    add(b,a,len);
            }
            Dij(1);
            printf("%d
    ",dist[n]);
        }
        return 0;
    } 

     如有错误和不足之处欢迎指点,谢谢大家~

    参考:

    http://www.cnblogs.com/hxsyl/p/3270401.html

    https://blog.csdn.net/qq_35644234/article/details/60870719

    https://blog.csdn.net/u012469987/article/details/51319574#dijkstra

  • 相关阅读:
    C# winform 学习(一)
    C# winform 学习(一)
    C# winform 学习(二)
    C# winform 学习(二)
    C# Winform 学习(四)
    C# Winform 学习(四)
    C# winform 学习(三)
    gcc编译动态和静态链接库
    对深拷贝与浅拷贝的再次理解(默认构造函数是浅拷贝)
    QWaitCondition(和Java的Notify机制非常相像)
  • 原文地址:https://www.cnblogs.com/LjwCarrot/p/9449706.html
Copyright © 2011-2022 走看看