Dijkstra算法求最短路径解析

问题描述

假设你是一个想环游世界的穷孩子,现在好不容易攒了些钱,想要去很多城市。但是由于资金有限,你得尽量找便宜的交通方式。但有的城市从你家根本不能直达,有些城市你从家坐高铁就能直达,有些城市从你家到那里只有飞机。现在你知道了许多城市之间的交通费,想要到目的地去,除了直达还能转车。转车比飞机便宜啊!所以你精打细算想看看从你家到各城市的最便宜的交通方式,这时候有一个叫Dijkstra的人告诉你一个方法,在知道你想去的各个城市之间费用的情况下,计算出从你家到这些城市的最便宜的交通费。这就是Dijkstra算法。

算法求解过程

假设一共有n个城市。
1.先用一张纸把这些城市的名字记录下来。再把这些城市里已经相互能直达的两个城市的费用记在账单上。再在黑板上写下到各个城市的最少费用,不能直达的城市之间就记成无穷大。准备好黑板擦,黑板只是个草稿,最少费用会在求解过程中实时改变。

int **Graph=new int*[n];      //账单,在这里我用二维数组来存这些城市之间的交通费
int *dist=new int[n];    //黑板,用来记录当前两个城市之间的最便宜费用
int *path=new int[n];    //记录是从哪个城市到这个城市的
bool *collected=new int[n];    //记录城市的名字的纸,用过之后就划掉

 

2.先从家出发,从黑板上挑出能最便宜直达另一个城市的路径,我们假设这个城市为A,一个小时公交车就能到。把家到A的交通费记在黑板上,因为A已经是从家到其他城市交通费里最低的了,所以这就是家到A的最便宜的交通方式。现在已经找过家了,就在纸上把家划掉。

3.现在从A出发,把从A能到达的城市找出来。依次比较家直达这些城市的费用,与从A转站到这些城市的费用。如果从A转站到达这些城市比直达还便宜的话,就把便宜的哪一种费用记在黑板上,把先导路径也记上,也就是到达此城市的前一站。如到B城市最偏宜的路径是从A转车,就记先导路径为A,反之记成家。

    while (1) 
    {
        int V = FindMinDist(collected, dist, n);
        if(V==-1)
        {
            break;      /* 算法结束 */
        }
        collected[V] = true;  /* 收录V */
        for(int j=0; j<n; j++ ) /* 对图中的每个顶点j */
            if ( j!=V&& Graph[V][j]<INFINITY ) 
            {
                //if ( Graph[V][j]<0 ) /* 若有负边 */
                //return false; /* 不能正确解决,返回错误标记 */
                /* 若收录V使得dist[j]变小 */
                if ( dist[V]+Graph[V][j] < dist[j] ) 
                {
                    dist[j] = dist[V]+Graph[V][j]; /* 更新dist[j] */
                    path[j]=V;/*更新路径*/
                }
            }
    } /* while结束*/
}

 

4.把A能到达的所有城市全布遍历,经过3中的比较之后,就在纸上把A划去。然后从黑板上挑出除A外下一个最便宜的路径,重复3中的操作。一直到纸上所有的城市都被划去。

5.现在黑板上所记录的就是家到各城市的最便宜交通费了。但是由于每个城市只记录了到达自己的前一站,所以如果想知道整条路的话,需要通过自己的前一站去找这个站的前面的站,一直遍历到家,才能知道全部路径。

使用范围

在有向有权中,找到由一个顶点 V0 到其余各点的最短路径长度

整体代码实现(下面用一个具体题目来体现)

题目:迪杰斯特拉最短路径算法(此题中没有用到路径,但我还是把用法写上去了)
题目描述

在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。

在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。

在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法求出源点至每一个其它顶点的最短路径长度。

输入

输入的第一行包含2个正整数n和s,表示图中共有n个顶点,且源点为s。其中n不超过50,s小于n。

以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0。

输出

只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。

请注意行尾输出换行。

样例输入

4 1
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0

样例输出

6 4 7

#include<iostream>
#include<limits.h>
#define INFINITY INT_MAX
using namespace std;

int FindMinDist(bool *collected,int *dist,int n)
{
    /* 返回未被收录顶点中dist最小者 */
    int MinV;
    int MinDist = INFINITY;
 
    for (int i=0; i<n; i++) {
        if ( collected[i]==false && dist[i]<MinDist) 
        {
            /* 若i未被收录,且dist[i]更小 */
            MinDist = dist[i]; /* 更新最小距离 */
            MinV = i; /* 更新对应顶点 */
        }
    }
    if (MinDist < INFINITY) /* 若找到最小dist */
        return MinV; /* 返回对应的顶点下标 */
    else return -1;  /* 若这样的顶点不存在,返回错误标记 */
}

void Dijkstra(int **Graph,int start,int *dist,bool *collected,int n,int *path)
{
    
    for (int i=0; i<n; i++ ) 
        {
            dist[i] = Graph[start][i];
            if(dist<INFINITY)
                path[i]=start;
            else
                path[i]=-1;
        }
    
    /* 先将起点收入集合 */
    collected[start]= true;     //收录设为0
 
    while (1) 
    {
        int V = FindMinDist(collected, dist, n);
        if(V==-1)
        {
            break;      /* 算法结束 */
        }
        collected[V] = true;  /* 收录V */
        for(int j=0; j<n; j++ ) /* 对图中的每个顶点j */
            if ( j!=V&& Graph[V][j]<INFINITY ) 
            {
               // if ( Graph[V][j]<0 ) /* 若有负边(但本题不涉及负边,所以不用) */
                //return ; /* 不能正确解决,返回 */
                /* 若收录V使得dist[j]变小 */
                if ( dist[V]+Graph[V][j] < dist[j] ) 
                {
                    dist[j] = dist[V]+Graph[V][j]; /* 更新dist[j] */
                    path[j]=V;/*更新路径*/
                }
            }
    } /* while结束*/
}

int main()
{
    int n,s;
    cin >> n >> s;
    int **Graph=new int*[n];
    int *dist=new int[n];       //原点到v的最短路径
    int *path=new int[n];
    bool *collected=new bool[n];
    for(int i=0;i<n;i++)
    {
        Graph[i]=new int[n];    //初始化图
        for(int j=0;j<n;j++)
        {
            cin>>Graph[i][j];
            if(Graph[i][j]==0)
                Graph[i][j]=INFINITY;    //不能到达或者是自己到自己就设为无线大
        }
        
        collected[i]=false;    
    }
    Dijkstra(Graph,s,dist,collected,n,path);
    for(int i=0;i<n;i++)    //输出源点至其它每一个顶点的最短路径长度
    {
        if(i==s)
            continue;
        if(dist[i]==INFINITY)
            cout<<-1<<" ";
        else
            cout<<dist[i]<<" ";
    }
    cout<<endl;
    return 0;
}

 

 

 
posted @ 2019-06-16 13:52  步行植物  阅读(3702)  评论(0编辑  收藏  举报