最爱的城市(卡码网第25题,最短路径的两种解法)

一. 题目描述

一天小明捧着一本世界地图在看,突然小明拿起笔,将他最爱的那些城市标记出来,并且随机的将这些城市中的某些用线段两两连接起来。
小明量出了每条线段的长度,现在小明想知道在这些线段组成的图中任意两个城市之间的最短距离是多少。

输入描述

输入包含多组测试数据。
每组输入第一行为两个正整数n(n<=10)和m(m<=n*(n-1)/2),n表示城市个数,m表示线段个数。
接下来m行,每行输入三个整数a,b和l,表示a市与b市之间存在一条线段,线段长度为l。(a与b不同)
每组最后一行输入两个整数x和y,表示问题:x市与y市之间的最短距离是多少。(x与y不同)
城市标号为1~n,l<=20。

输出描述

对于每组输入,输出x市与y市之间的最短距离,如果x市与y市之间非连通,则输出“No path”。

输入示例
4 4
1 2 4
1 3 1
1 4 1
2 3 1
2 4
输出示例
3

二. 解题思路

本题就是求两个节点之间的最短路径,在数据结构中有dijsktra和floyd两种求最短路径的方法,下面分别对这两种方法进行求解本题:

1. dijsktra解法:

int dijsktra(int x, int y, vector<vector<int>>& graph){
    int num = graph.size();
    vector<int> dis(num, 99);
    vector<int> final(num, 0);
    for(int i = 0; i < num; i++){
        dis[i] = graph[x][i];
    }
    for(int i = 0; i < num; i++){
        int u = 0, d = 100;
        for(int j = 0; j < num; j++){
            if(final[j] == 0 && dis[j] < d){
                d = dis[j];
                u = j;
            }
        }
        final[u] = 1;
        
        for(int j = 0; j < num; j++){
            if(final[j] == 0){
                dis[j] = min(dis[j], dis[u] + graph[j][u]);
            }
        }
    }
    return dis[y];
}
  1. 初始化
    • num 是图中节点的数量。
    • dis 是一个数组,用于存储从源点 x 到每个节点的当前最短距离,初始时除了到 x 自身的距离(理论上应为0,但这里从 graph[x][i] 开始,这可能不正确,除非 graph[x][x] 确实为0且图是有向的),其他都设为一个大数(99)。
    • final 是一个标记数组,用于记录节点是否已被处理过(即最短路径是否已确定)。
  2. 寻找最小距离节点
    • 在每次迭代中,算法寻找一个未处理的节点 u,它当前具有最小的 dis[u] 值。
    • 这里有一个问题:如果 graph[x][i] 初始化为 graph 中 x 行对应的值,那么如果 graph 是一个邻接矩阵且包含未直接相连节点间的大值(如 INF),则这个初始化可能不正确,因为即使 x 和 i 不直接相连,dis[i] 也应该被设置为一个足够大的数(这里已经做了,但初始化方式可能不是最优的)。
  3. 更新距离
    • 一旦选择了节点 u,算法会尝试通过 u 来更新其他未处理节点的最短距离。这是通过检查 dis[u] + graph[j][u] 是否小于当前的 dis[j] 来实现的。
    • 注意这里 graph[j][u] 的使用,它假设图是有向的,并且 u 指向 j 的边权重存储在 graph[j][u] 中。这在某些图表示中是正确的(特别是当图以邻接矩阵的转置形式存储时),但在标准邻接矩阵表示中,你可能会期望看到 graph[u][j]

2. floyd解法:

int floyd(int x, int y, vector<vector<int>>& graph){
    int num = graph.size();
    vector<vector<int>> dis;
    dis = graph;
    for(int i = 0; i < num; i++){
        for(int s = 0; s < num; s++){
            for(int t = 0; t < num; t++){
                dis[s][t] = min(dis[s][t], dis[s][i] + dis[i][t]);
            }
        }
    }
    return dis[x][y];
}
  1. 初始化
    • int num = graph.size(); 获取图中节点的数量。
    • vector<vector<int>> dis; 声明一个二维向量 dis 来存储最短路径的长度。
    • dis = graph; 将输入的图 graph 复制到 dis 中,作为最短路径长度的初始值。这里假设 graph 已经包含了节点之间的直接距离(如果两个节点不直接相连,则相应位置应该是一个足够大的数,表示无穷大或不可达)。
  2. 三重循环
    • 外部两层循环(is)遍历图中的每个节点作为中间节点。
    • 内部循环(t)遍历图中的每个节点作为目标节点。
    • 在三重循环内部,通过比较 dis[s][t](当前从 s 到 t 的最短路径长度)和 dis[s][i] + dis[i][t](通过中间节点 i 从 s 到 t 的路径长度),来更新 dis[s][t]。如果后者更小,则更新 dis[s][t],表示找到了一个更短的路径。
  3. 返回结果
    • 函数返回 dis[x][y],即从节点 x 到节点 y 的最短路径长度。

三. 代码

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int dijsktra(int x, int y, vector<vector<int>>& graph){
    int num = graph.size();
    vector<int> dis(num, 99);
    vector<int> final(num, 0);
    for(int i = 0; i < num; i++){
        dis[i] = graph[x][i];
    }
    for(int i = 0; i < num; i++){
        int u = 0, d = 100;
        for(int j = 0; j < num; j++){
            if(final[j] == 0 && dis[j] < d){
                d = dis[j];
                u = j;
            }
        }
        final[u] = 1;
        
        for(int j = 0; j < num; j++){
            if(final[j] == 0){
                dis[j] = min(dis[j], dis[u] + graph[j][u]);
            }
        }
    }
    return dis[y];
}

int floyd(int x, int y, vector<vector<int>>& graph){
    int num = graph.size();
    vector<vector<int>> dis;
    dis = graph;
    for(int i = 0; i < num; i++){
        for(int s = 0; s < num; s++){
            for(int t = 0; t < num; t++){
                dis[s][t] = min(dis[s][t], dis[s][i] + dis[i][t]);
            }
        }
    }
    return dis[x][y];
}

int main(){
    int n, m;
    while(cin >> n >> m){
        vector<vector<int>> graph(n + 1, vector<int>(n + 1, 99));
        for(int i = 0 ; i< m; i++){
            int a, b, l;
            cin >> a >> b >> l;
            graph[a][b] = l;
            graph[b][a] = l;
        }
        
        for(int i = 1; i <= n; i++){
            graph[i][i] = 0;
        }
        
        int x, y;
        cin >> x >> y;
        // int result = dijsktra(x, y, graph);
        int result = floyd(x, y, graph);
        if(result == 99){
            cout << "No path" << endl;
        }
        else{
            cout << result << endl;
        }
    }
}

四. 总结

这类题目是数据结构的基础概念,个人感觉是必须要掌握的,虽然这类题的考法就几种,但是掌握了之后对自己以后算法之类的学习还是有很大帮助的,大家一起加油!!!

喜欢的话给个关注吧!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值