Dijkstra(迪杰斯特拉)算法求最短路径

题目:爷爷奶奶的幸福生活 

【问题描述】

张三和李四夫妇退休后,去国外风景秀美的H市买了一套房子准备安度晚年。住了一段时间后,他们对当地的交通还是不太了解,有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车。请你写一个程序帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。

【输入形式】

第一行是公交车的总数N(0<=N<=10000);

第二行是张三的所在地start,目的地end

接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30字符串)。

note:一组数据中地名数不会超过150个。

【输出形式】

如果张三能到达目的地,第一行输出最短的时间,第二行输出最短时间的路径;否则,输出“No path.”。

【样例输入】

5

Leeds Edinburgh

Glasgow Edinburgh 70

Leeds Glasgow 10

Bristol Edinburgh 40

Leeds Edinburgh 65

Leeds Bristol 20

【样例输出】

60

Leeds -> Bristol ->Edinburgh

后续:虽然偶尔会迷路,但是因为有了你的帮助,张三和李四从此还是过上了幸福的生活。 

――全剧终――


算法思路:

Dijstra算法用来求一个顶点到其他顶点间的最短路径,而Floyd算法适用于求任意两个点间的最短路径。

Dijstra可用类,也可用结构体。本文为简化代码选择结构体,有时间的话会补充类的代码。

这个题的一个难点在于如何将字符串存入顶点数组中,我们采用string数组来解决这个问题。

定义S集合和U集合,每次循环一遍,找到U集合中dist数组中最小值点k加入到S集合中,比较起点是否能通过从起点到k再到终点找到更短的路径,若找得到则更新dist和path数组。

时间复杂度为O(n2)。

推荐视频: 最短路径(Dijskra算法) 作者:懒猫老师 https://www.bilibili.com/read/cv8013121/?from=search&spm_id_from=333.337.0.0 出处:bilibili。

#include <bits/stdc++.h>
#include <iostream>
#include <string.h>
#define INF 0x3f3f3f3f
#define MAX 32

using namespace std;

//邻接矩阵图
struct MGraph
{
    int vexNum; // 顶点个数
    string vertex[5*MAX]; // 存储顶点信息,即每个公交站的名字
    int arcNum; // 边的个数
    int arc[MAX][MAX]; // 邻接矩阵
};

//Dijstra结构体
struct Dijstra
{
    int s[MAX]={0}; // S集合
    int dist[MAX]={0}; // dist数组存储源点到各顶点的距离
    int path[MAX]={0}; // path数组存储该点上一个顶点的下标
    string start, end; // 张三的起点和终点
};

bool isdiffer(string str, string arr[], int n); // 判断字符串是否和字符串数组中的元素重复,即获得vertex数组
int getIndex(string str, string arr[], int n); // 获取vertex的下标
int getMin(int *s, int dist[], int n); // 获得U集合里元素dist的最小值
void inDijstra(MGraph G, Dijstra &D); // Dijstra算法,获得dist和path数组
bool input(MGraph &G, Dijstra &D); // 输入函数
void display(MGraph G, Dijstra D); // 输出答案

/**判断字符串是否和字符串数组中的元素重复,用于获得vertex数组*/
bool isdiffer(string str, string arr[], int n)
{
    bool flag = true;
    for(int i=0; i<n; i++)
    {
        if(str == arr[i])
        {
            flag = false;
            break;
        }
    }
    return flag;
}

/**获取vertex的下标*/
int getIndex(string str, string arr[], int n)
{
    for(int i=0; i<n; i++)
    {
        if(str == arr[i])
            return i;
    }
    return -1;
}

/**获得U集合里元素dist的最小值*/
int getMin(int *s, int dist[], int n)
{
    int min = INF;
    int minindex = -1;
    for(int i=0; i<n; i++)
    {
        // 如果在U集合中且值小于min
        if(s[i] == 0 && dist[i] < min)
        {
            min = dist[i];
            minindex = i;
        }
    }
    return minindex;
}

/**Dijstra算法,获得dist和path数组*/
void inDijstra(MGraph G, Dijstra &D)
{
    int startindex = getIndex(D.start, G.vertex, G.vexNum); // 源点下标,即start对应的vertex数组下标
    // S集合初始化
    for(int i=0; i<G.vexNum; i++)
    {
        D.s[i] = 0;
    }
    D.s[startindex] = 1;
    // dist、path数组初始化
    for(int i=0; i<G.vexNum; i++)
    {
        D.dist[i] = G.arc[startindex][i];
        if(D.dist[i] != INF)
            D.path[i] = startindex;
        else
            D.path[i] = -1;
    }
    int n = 1; // 表示加入到S集合中的个数
    while(n < G.vexNum) // 有顶点在U集合中则循环
    {
        // 最小值对应的dist数组的下标
        int min = getMin(D.s, D.dist, G.vexNum);
        if(min == -1) // 没有找到最小值,说明U集合中的点对应的dist均为INF,即两点之间没有路径
            break;
        D.s[min] = 1; // 将找到的点加入到S集合中
        n++;
        // 进入循环,更新dist和path数组
        for(int i=0; i<G.vexNum; i++)
        {
            if(G.arc[min][i] != 0 && G.arc[min][i] != INF)
            {
                if(D.dist[i] > D.dist[min] + G.arc[min][i])
                {
                    D.dist[i] = D.dist[min] + G.arc[min][i];
                    D.path[i] = min;
                }
            }
        }
    }
}

/**输入函数*/
bool input(MGraph &G, Dijstra &D)
{
    int num; // 公交车总数
    cin >> num;
    // 检查数据是否有效
    if(num < 0 || num > 10000)
    {
        cout << "N is invalid!\n";
        return false;
    }
    G.arcNum = num;
    cin >> D.start >> D.end;
    // 检查数据是否有效
    if(D.start.size() > MAX || D.end.size() > MAX)
    {
        cout << "Start or end is too long!\n";
        return false;
    }
    string from, to; // 公交站的起点站和终点站
    int time; // 公交车从from到to站所花的时间
    int v=0;
    // G.arc初始化
    for(int i=0; i<MAX; i++)
    {
        for(int j=0; j<MAX; j++)
        {
            if(i == j)
                G.arc[i][j] = 0;
            else
                G.arc[i][j] = INF;
        }
    }
    // G.vexNum赋初值为0
    G.vexNum=0;
    for(int i=0; i<num; i++)
    {
        cin >> from >> to >> time;
        // 检查数据是否有效
        if(from.size() > MAX || to.size() > MAX)
        {
            cout << "From or to is too long!\n";
            return false;
        }
        // 如果公交站起点没有出现过,则加入到vertex数组中
        if(isdiffer(from, G.vertex, G.vexNum))
        {
            G.vertex[v++] = from;
            G.vexNum++;
        }
        // 如果公交站终点没有出现过,则加入到vertex数组中
        if(isdiffer(to, G.vertex, G.vexNum))
        {
            G.vertex[v++] = to;
            G.vexNum++;
        }
        // 改变对应的arc数组的值
        G.arc[getIndex(from, G.vertex, G.vexNum)][getIndex(to, G.vertex, G.vexNum)] = time;
    }
    return true;
}

/**输出结果*/
void display(MGraph G, Dijstra D)
{
    int endindex = getIndex(D.end, G.vertex, G.vexNum); // 终点下边,即end对应的vertex数组下标
    // 若path为-1,则表示没有路径
    if(D.path[endindex] == -1)
    {
        cout << "No path.\n";
        return;
    }
    cout << D.dist[endindex] << endl;
    string tmp[G.vexNum]; // 暂存结果
    int i = endindex, v=0;
    while(i != D.path[i])
    {
        tmp[v++] = G.vertex[i];
        i = D.path[i];
    }
    i = D.path[i];
    cout << G.vertex[i];
    // 因为tmp是逆序存入的,所以逆序输出结果
    for(int i=v-1; i>=0; i--)
        cout << "->" << tmp[i];
}

int main()
{
    MGraph G; // 图结构体
    Dijstra D; // Dijstra结构体
    bool flag = input(G, D); // 调用输入函数
    //如果输入有误,则返回0
    if(!flag)
        return 0;
    inDijstra(G, D); // 调用Dijstra函数
    display(G, D); // 输出结果
    return 0;
}

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值