求单点的最短路径

题目信息

求从指定源点出发到各个顶点的最短路径。
在这里插入图片描述
假设图中结点名均为单个互不相同的字母,权值均>0。

输入

第一行:结点数量n,弧数量e,源点
后续e行:<结点,结点,权值>

输出

按结点的升序输出到达各个结点的最短路径长度

测试样例

测试样例1

7,10,a
<a,b,13>
<a,c,8>
<c,d,5>
<d,e,6>
<a,e,30>
<a,g,32>
<b,g,7>
<b,f,9>
<e,f,2>
<f,g,17>
a:0
b:13
c:8
d:13
e:19
f:21
g:20

测试样例2

5,9,a
<a,b,10>
<b,a,20>
<a,d,50>
<a,e,45>
<b,c,15>
<c,d,20>
<c,e,5>
<d,e,10>
<e,c,30>
a:0
b:10
c:25
d:45
e:30

测试样例3

5,7,a
<a,b,10>
<b,c,50>
<a,d,30>
<a,e,100>
<c,e,10>
<d,c,20>
<d,e,60>
a:0
b:10
c:50
d:30
e:60

解答

#include<iostream>

using namespace std;
const int inf = 0x3f3f3f;
int road[1010][1010], d[1010];
bool vis[1010];
int n, m;

void dijkstra(int s)
{//s是当前的点
    for (int i = 0; i < n; i++)
    {//对所有的点进行遍历,同时将距离拷贝到d[]中
        d[i] = road[s][i];
        vis[i] = false;
    }
    //自己对自己距离为0,同时标记为定值
    d[s] = 0;
    vis[s] = true;
    for (int i = 0; i < n; i++)
    {//对每一个点进行操作
        int temp = inf, v;
        for (int j = 0; j < n; j++)
        {//此循环相当于找到了未确认的点的最小值点,temp记录着最小值,v记录着那个点
            if (!vis[j] && temp > d[j])
            {
                temp = d[j];
                v = j;
            }
        }
        if (temp == inf)
        {//如果没找到最小值则直接结束
            break;
        }
        vis[v] = true;//此时这个点就是我们能确定的了
        for (int j = 0; j < n; j++)
        {//未确定的点中,那个点加上他俩之间点距离如果小于之前缓存的值,那么就更新一下这个值
            if (!vis[j] && d[v] + road[v][j] < d[j])
            {
                d[j] = d[v] + road[v][j];
            }
        }
    }
}

int main()
{
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    char c, ch;
    cin >> m >> ch >> n >> ch >> c;
    for (int i = 0; i < n; i++)
    {//初始化
        for (int j = 0; j < n; j++)
        {
            if (i == j)
            {
                road[i][j] = 0;
            }
            else
            {
                road[i][j] = inf;
            }
        }
    }
    for (int i = 0; i < n; i++)
    {
        int dis;
        char a, b;
        cin >> ch >> a >> ch >> b >> ch >> dis >> ch;
        int x = a - 'a';//把字符转化为数字来算
        int y = b - 'a';
        if (road[x][y] > dis)
        {//相当于一直在让road[x][y]保持最小值
            road[x][y] = dis;
        }
    }
    int num = c - 'a';//同样把字符转化为数字来算
    dijkstra(num);
    for (int i = 0; i < m; i++)
    {
        cout << (char) (i + 'a') << ":" << d[i] << endl;
    }
    return 0;
}

想法

Floyd适合计算多源最短路,Dijkstra、Bellman-Ford、SPFA适合单源最短路。Dijkstra不能处理权值为负的情况,而 Bellman-Ford 可以处理负权值,SPFA是对 Bellman-Ford 的计算优化。
本题可以看出是很经典的单源最短路问题,且权值均大于0,可以采用常用的 Dijkstra 算法来计算。

Dijkstra算法特点

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

Dijkstra算法思路

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

Dijkstra算法演示

下面求下图,从顶点v1到其他各个顶点的最短路径
在这里插入图片描述
首先第一步,我们先声明一个d数组,该数组初始化的值为:

V1V2V3V4V5V6
d01030100
我们首先先将V1设置为已经确定的点
既然是求 v1 顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 d 可知当前离v1顶点最近是 v3顶点。当选择了 2 号顶点后,d[2](下标从0开始)的值就已经从“估计值”变为了“确定值”,即 v1顶点到 v3顶点的最短路程就是当前 d[2]值。将V3标记为确定的点。
为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1 顶点到 v3 顶点的路程进一步缩短了。因为 v1 顶点到其它顶点的路程肯定没有 v1 到 v3 顶点短.
OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3会有出度,发现以v3 为弧尾的有:< v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比v1–v4短,其实这个已经是很明显的了,因为d[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新d[3]的值,得到如下结果:
V1V2V3V4V5V6
d0106030100
因此d[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 d[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。
然后,我们又从除d[2]和d[0]外的其他值中寻找最小值,发现d[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是d[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组d的值,v5有两条出度:< v5,v4> 和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而d[3]的值为60,所以我们要更新d[3]的值.另外,v1-v5-v6的长度为:90,而d[5]为100,所以我们需要更新d[5]的值。更新后的d数组如下图:
V1V2V3V4V5V6
d010503090
然后,继续从d中选择未确定的顶点的值中选择一个最小的值,发现d[3]的值是最小的,所以把v4 标记为确定的点后,考虑v4的出度是否会影响我们的数组d的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而d[5]的值为90,所以我们要更新d[5]的值,更新后的d数组如下图:
V1V2V3V4V5V6
d010503060

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

起点终点路径长度
V1V1-0
V1V2-
V1V3V1 -> V3
V1V4V1 -> V5 -> V4
V1V5V1 -> V5
V1V6V1 -> V5 -> V4 -> V6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值