最短路径:Floyd和Dijkstra算法

1.Floyd算法

Floyd算法本质上是动态规划,即对于每一对结点,不断更新其以某一结点为中间结点的最小值。关于算法理解可以参考以下两篇文章:
Floyd算法的详细过程
理解为什么每次循环时d[i][j]一定取到了最小值
代码即为简单的三重循环

for(int k=1;k<=n;k++)  
    for(int i=1;i<=n;i++)  
    	for(int j=1;j<=n;j++)  
    		if(d[i][j]>d[i][k]+d[k][j])  
                d[i][j]=d[i][k]+d[k][j];

时间复杂度:O(N^3)
空间复杂度:O(N^2)
适用于全源最短路问题(能确定所有结点之间的最短路)

在大多数机试时间范围内,使用Floyd算法要求图的大小不超过200个结点

可以看出,Floyd算法实现起来非常方便,但是由于其计算了所有节点之间的最短路,因此浪费了不少时间。所以,接下来介绍Dijkstrta算法。

2.Dijkstra算法

在这里插入图片描述
Dijkstrta算法算法的基本思想是,从起点开始作为标准点,选择与之距离最近的点作为新的标准点,然后更新其他点至起点的最短距离,依次选择至最后一个点。
在这里插入图片描述
在这里插入图片描述
图片来源点击跳转

时间复杂度:O(N^2)(堆优化后可以降至O(N*logN))
空间复杂度:O(N)
适用于单源最短路问题

下面是一道王道中的简单例题,分别使用两种算法来解决,加深一下对于两种算法的理解:

Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
Sample Output
3
2

使用Floyd解法:

#include<iostream>
using namespace std;
int ans[101][101];
int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        if (n == 0 && m == 0)
            break;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                ans[i][j] = -1;  // 对邻接矩阵初始化,我们用-1代表无穷
            }
            ans[i][i] = 0;   // 自己到自己的路径长度设为0
        }
        while (m--) {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            ans[a][b] = ans[b][a] = c;
        }
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (ans[i][k] == -1 || ans[k][j] == -1)
                        continue;
                    if (ans[i][j] == -1 || ans[i][k] + ans[k][j] < ans[i][j])
                        ans[i][j] = ans[i][k] + ans[k][j];
                }
            }
        }
        printf("%d\n", ans[1][n]);
    }
    return 0;
}

使用Dijkstra解法:

#include <vector>
#include <iostream>
using namespace std;
struct E {   // 邻接链表中链表元素结构体
    int next;   // 代表直接相邻的节点
    int c;      // 代表该边的权值(长度)
};

vector<E> edge[101];        // 邻接链表
bool mark[101];  // 标记,当mark[j]为true时表示节点j的最短路径长度已经得到,该节点已经加入集合K
/*距离向量,当mark[i]为true时,表示已得到的最短路径长度;
 否则,表示所有从节点1出发,经过已知的最短路径达到集合K中的某节点,再经过一条边到达节点i的路径中最短的距离
 */
int Dis[101];
int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF){
        if (n == 0 && m== 0)
            break;
        for (int i = 1; i <= n; i++)
            edge[i].clear();     // 初始化邻接链表
        while (m--) {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            E tmp;
            tmp.c = c;
            tmp.next = b;
            edge[a].push_back(tmp);
            tmp.next = a;
            edge[b].push_back(tmp);
        }
        for (int i = 1; i <= n; i++) {  // 初始化
            Dis[i] = -1;      // 所有距离为-1,即不可达
            mark[i] = false;   // 所有节点不属于集合K
        }
        Dis[1] = 0;    // 得到最近的点为节点1,长度为0
        mark[1] = true;   // 将节点1加入集合K
        int newP = 1;     // 集合K中新加入的点为节点1
        for (int i = 1; i < n; i++) {  // 循环n-1次,按照最短路径递增的顺序确定
            for (int j = 0; j < edge[newP].size(); j++) {
                int t = edge[newP][j].next;     // 该边的另一个节点
                int c = edge[newP][j].c;       // 该边的长度
                if (mark[t] == true)        // 若另一节点也属于K,则跳过
                    continue;
                // 若该店尚不可达,或者该节点从新加入的节点经过一条边到达时比以往距离更短
                if (Dis[t] == -1 || Dis[t] > Dis[newP] + c)
                    Dis[t] = Dis[newP] + c;
            }
            int min = 123123123;    // 最小值初始化为一个大整数,为找最小值做准备
            for (int j = 1; j <= n; j++) {
                if (mark[j] == true)
                    continue;
                if (Dis[j] != -1 && Dis[j] < min) {
                    min = Dis[j];
                    newP = j;
                }
            }
            mark[newP] = true;
        }
        // printf("%d %d\n", Dis[T], cost[T]);  // 输出答案
    }
    return 0}

猜你喜欢

转载自blog.csdn.net/weixin_43835911/article/details/88967702