欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

最短路径 Dijkstra

程序员文章站 2023-12-22 17:58:46
...

例子是别人那里的,分析也是,代码是BIshop 姚英杰的。


1、最短路径问题介绍

问题解释:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径

解决问题的算法:

这篇博客,我们就对Dijkstra算法来做一个详细的介绍

2、Dijkstra算法介绍

  • 算法特点:

    迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

  • 算法的思路

    Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
    然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,
    然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
    然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

3、Dijkstra算法示例演示

下面我求下图,从顶点v1到其他各个顶点的最短路径

最短路径 Dijkstra

首先第一步,我们先声明一个dis数组,该数组初始化的值为:
最短路径 Dijkstra

我们的顶点集T的初始化为:T={v1}

既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点。当选择了 2 号顶点后,dis[2](下标从0开始)的值就已经从“估计值”变为了“确定值”,即 v1顶点到 v3顶点的最短路程就是当前 dis[2]值。将V3加入到T中。
为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短.

OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3会有出度,发现以v3 为弧尾的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比v1–v4短,其实这个已经是很明显的了,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果:
最短路径 Dijkstra

因此 dis[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 dis[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。

然后,我们又从除dis[2]和dis[0]外的其他值中寻找最小值,发现dis[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而dis[5]为100,所以我们需要更新dis[5]的值。更新后的dis数组如下图:
最短路径 Dijkstra

然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v5,v4>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图:
最短路径 Dijkstra

然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下:
最短路径 Dijkstra

因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:

起点  终点    最短路径    长度
v1    v2     无          ∞    
      v3     {v1,v3}    10
      v4     {v1,v5,v4}  50
      v5     {v1,v5}    30
      v6     {v1,v5,v4,v6} 60


姚英杰学长的代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define maxn 111
#define INF 0x3f3f3f3f

int mat[maxn][maxn];
int vis[maxn];
int dist[maxn];

int st,ed;
int n,m;

int main()
{
    scanf("%d%d",&n,&m);
    int u,v,w;
    int i,j;
    memset(mat,0,sizeof(mat));
    for(i = 0;i < m; i++)
    {
        scanf("%d%d%d",&u,&v,&w);            // w > 0
        mat[u][v] = mat[v][u] = w;           //无向图
    }
    for(i = 0;i < n; i++)
    {
        vis[i] = 0,dist[i] = INF;
    }
    scanf("%d%d",&st,&ed); //输出起点,终点
    dist[st] = 0;          //起点到自身的距离为0                 
    for(i = 1;i < n; i++)
    {
        int minn = INF;
        int min_index;
        for(j = 0;j < n; j++) //选出在右边集合中,距离最小的节点
        {
            if(!vis[j] && dist[j] < minn)
            {
                minn = dist[j];
                min_index = j;
            }
        }
        if(minn == INF) break;
        vis[min_index] = 1;   //标记已经访问过的,意即加入左边的集合
        for(j = 0;j < n; j++) //更新那些右边集合中,通过min_index这个点可以到达点的距离
        {
            if(!vis[j] && dis[j] > dist[min_index] + mat[min_index][j])
            {
                dist[j] = dist[min_index] + mat[min_index][j];
            }
        }
    }
    printf("%d\n",dist[ed]);
    
    return 0;
}
/*首先输入两个数n和m,表示顶点数和变数。
 *接下来是m行,每行三个数u,v,w,表示有一条连接u和v的边,边长是w(无向边)
 *最最后一行是两个整数,st,ed,起点与终点
 *输出从st到ed的最短路径长度
 *每个顶点的编号大于等于0,小于等于n-1
 */ 


衍生题目

Problem Description


Wallace is an undergraduate, and he is really tall. After a long term’s study, he is about to go home. In Wallace’s country, there are n cities (numbered form 1 to n) and m bidirectionalroutes (numbered from 1 to m). The i-th route starts in city numbered by ui and ends in city numbered by vi, while it takes Wallace ci hours to travel. And now, we know Wallace’s university is in the city numbered by S, and his home is in the city numbered by E. Because Wallace is a lazy boy, he wants to minimize the number of times of changing routes. If two solutions have the same number of times of changing routes, then Wallace wants to spend as less time as possible.


Input


The first line contains an integer T (1<=T<=10), then T cases follow.


In each case:


The first line contains 2 integers: n (1<=n<=1000) and m (1<=m<=10000).


Then m lines follow, each line contains 3 numbers: ui, vi and ci (1<=ui, vi<=n, ui≠vi, 1<=ci<=100).


Then one line contains 2 integers: S and E(1<=S, E<=n, S≠E).


Output


For the i-th case, first output one line “Case #i:”.


If Wallace can’t get home, then output “WTF?” (without quotes). Otherwise, output one line contains two integers separated by a space. The first integer is the minimum number of times of changing routes, and the second integer is the minimum time Wallace needs to spend when minimizing the number of times of changing routes.


Sample Input

2
5 5
1 2 1
2 4 1
4 5 1
1 3 100
3 5 100
1 5
3 1
1 2 5
1 3

Sample Output

Case #1:
2 200
Case #2:
WTF?



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define maxn 1111
#define INF 0x3f3f3f3f

int mat[maxn][maxn];
int vis[maxn];
int dist[maxn];
int num[maxn];

int st,ed;
int n,m;

int c_min(int a,int b)
{
    return a < b ? a : b;
}
void dijkstra()
{
    int i,j;
    dist[st] = 0;          //起点到自身的距离为0
    vis[st] = 1;
    num[st] = 0;
    for(i = 1;i <= n; i++){
        if(mat[st][i] != INF){
            dist[i] = mat[st][i];
            num[i] = 1;
        }
    }

    for(i = 1; i <= n; i++)
    {
        int minn = INF;
        int min_index;
        for(j = 1; j <= n; j++) //选出在右边集合中,距离最小的节点
        {
            if(!vis[j] && num[j] < minn)
            {
                minn = num[j];
                min_index = j;
            }
        }
        if(minn == INF) break;
        vis[min_index] = 1;   //标记已经访问过的,意即加入左边的集合
        for(j = 1; j <= n; j++) //更新那些右边集合中,通过min_index这个点可以到达点的距离
        {
            if(!vis[j] && num[j] >= 1 + num[min_index] && mat[min_index][j] != INF)
            {
                num[j] = num[min_index] + 1;
                dist[j] = c_min(dist[j],dist[min_index] + mat[min_index][j]);
            }
        }
    }
}
int main()
{
   int t,i,j;
   int icase = 0;
   scanf("%d",&t);
   while(t--)
   {
       for(i = 0;i <1004;i++)
       {
           vis[i] = 0;
           dist[i] = INF;
           num[i] = INF;
       }
       for(i = 0;i < 1004;i++)
       {
           for(j = 0;j < 1004; j++)
               mat[i][j] = INF;
       }
       scanf("%d%d",&n,&m);
       for(i = 0;i < m; i++)
       {
           int u,v,w;
           scanf("%d%d%d",&u,&v,&w);
           mat[u][v] = c_min(mat[u][v],w);
           mat[v][u] = mat[u][v];
       }
       scanf("%d%d",&st,&ed);
       dijkstra();
       printf("Case #%d:\n",++icase);
       if(num[ed] == INF) printf("WTF?\n");
       else printf("%d %d\n",num[ed],dist[ed]);
   }

    return 0;
}



相关标签: 算法 dijkstra

上一篇:

下一篇: