【ACM之路】5.最短路算法-Dijkstra算法

简介:

Dijkstra算法是一种单源路径算法。时间复杂度为:O(n^2).比Floyd算法(O(n^3)快很多。当然,Dijkstra算法可以用堆优化后,算法复杂度成了(O(m+n)logn),复杂度更底了。本文只讲解Dijkstra的简单算法。

问题:

       给予n个城市和m条道路,求从城市1到城市n的最短距离。(可见POJ2387,5个城市,5条道路)

【ACM之路】5.最短路算法-Dijkstra算法_第1张图片

思路:

      与Floyd算法点松弛不同,Dijkstra算法通过松弛边的做法找到从某一个点到其他各个点的最小值。

      比如我们求从点1到其他点的最短路径。则设立一个dist数组表示从1到其他点的距离为 0 20 ∞ ∞ ∞ 100.

      则,我们在这里面先找到最短的路径。20,则20这个点必然是1号到2号的最短路径,这个点的值确定了。接下来,我们在根据这个已经确定的点,算其他边,点2的出边有3,比较1到3的距离原来是∞,然后1-2-3的距离变成20+30 = 50 (这个值小于∞,故可以更新这个点。即(dist数组值变为:0 20 50 ∞ ∞ 100,)即把所有可能的出边通过20这个最小距离,进行松弛比较,判断哪个离得更近,选哪个。但是还有一个要注意的是,1号点和2号点已经确定了,所以我们松弛的时候就不用看着两个点了。怎么区分这个点是否确定了呢,我们可以用一个数组vis来表示,如果看过了就设为1,否则为0。直到所有的点都被确定了。那么dist数组也就确定了,即1号点到其他每个顶点的最短距离。

步骤:(以从点1到点N为例)

1.初始化,构造一个二维数组 map 存取地图路径长度,vis数组表示这个点是否访问过,dist数组表示从访问的起点到其他每一个点的距离。

【ACM之路】5.最短路算法-Dijkstra算法_第2张图片【ACM之路】5.最短路算法-Dijkstra算法_第3张图片

2.接下来,输入每条路径的值,更新到map中。其次,dist数组也会更新成map[ 1 ][  j  ]相应的内容,即1号顶点到其他顶点的初始距离。  

【ACM之路】5.最短路算法-Dijkstra算法_第4张图片【ACM之路】5.最短路算法-Dijkstra算法_第5张图片

3.先找dist数组中最小值的点,循环从1开始,但是这里的0是没有意义的。没有起到松弛作用。标记vis[1] = 1。接下来找未访问过的,且是dist中的最小值。找到了点2,路径是20,则20是1到2的最短路径,确定了。(因为没有比从1到其他点,再到2更短的路径了,前提是没有负权值)。20确定后,vis[ 2 ]设为1,表示已经访问过了。然后再对3,4,5这三个点进行比较,看从1到3近还是1->2,2->3近,由dist知,1->3为∞,1->2->3为50,故50<无穷大, 则更新dist,同样对1->4,1->2->4 和 1->5 ,1->2->5进行比较,发现更大了,故dist不更新。第二轮更新完毕。(第一轮是从vis[1] = 1开始,没有太大意义)。

【ACM之路】5.最短路算法-Dijkstra算法_第6张图片【ACM之路】5.最短路算法-Dijkstra算法_第7张图片【ACM之路】5.最短路算法-Dijkstra算法_第8张图片

       同样第三轮,在找到  没有被访问过的顶点且dist最小值  即50,则令vis[3] = 1,标记访问过且判断其他未访问过的顶点,能否松弛。发现,1->4由无穷大更新到了1->3->4 dist更新为70。

       后面同理。直到所有的顶点都访问过,dist的最后的值即为1号定点到其他顶点的最小值。

更新结果如上图所示。:

代码详解:

 

#include 
#include 
#include 
#define mem(a,b) memset(a,b,sizeof(a))
#define INF 1<<29

using namespace std;

int map[1010][1010];
int dist[1010]; //从1到各个点的距离
int vis[1010];  //vis标志这个点有没有被访问过

void Dijkstra(int n)
{
    int i,j,k,min;
    for(i=1;i<=n;i++)
    {
        dist[i] = map[1][i];
    }


    for(i=1;i<=n;i++)
    {
        k = 0;
        min = INF;
        //先找最短的路,要求该点没有被访问过且是距离的最小值
        for(j=1;j<=n;j++)
        {
            if(vis[j]==0 && dist[j] < min)  //标记3:忘了加vis[j]==0结果,只执行了一遍。
            {
                min = dist[j];
                k = j;
            }

        }
        vis[k] = 1; //标志k被访问
        for(j=1;j<=n;j++)
        {
            if(vis[j]==0 && dist[j]>dist[k]+map[k][j] )
                dist[j] = dist[k] + map[k][j];
        }
    }

}

int main()
{
    int i,j,a,b,c;
    int t,n;    //这里顺序坑人了,注意t是路径个数,n是地点个数
    while(cin >> t >> n)
    {
        //初始化
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(i==j)map[i][j] = 0;
                else map[i][j] = INF;
            }
        }
        mem(dist,0);
        mem(vis,0);

        //Input
        for(i=1;i<=t;i++)
        {
            cin >> a >> b >> c;
            if( c < map[a][b])      //标记1:写反了,写成c>map[a][b];
            {
                map[b][a] = map[a][b] = c;  //标记2:忘了写相反的路,即是无向图

            }
        }

        //Dijkstra算法
        Dijkstra(n);

        //Output
        printf("%d\n",dist[n]);


    }

    return 0;
}

POJ2387 Til the cows come home 注意的点:

1.这是无向图,所以记得输入地图信息,双向填写。

2.比原来更小的值才更新,防止重复路径。

3.注意找dist中的最小值时必须要求①。该点没有被访问过,②。最小值。

4.本题略坑的一点是注意城市和路径顺序,WA了好几次,终于找到出错点了。

 

你可能感兴趣的:(ACM之路)