📜  门|门 CS 1999 |第 38 题(1)

📅  最后修改于: 2023-12-03 15:12:46.089000             🧑  作者: Mango

门|门 CS 1999 |第 38 题

简介

这是一道来自于门|门 CS 1999考试的编程题目,题目编号为第38题。该题目需要求解两个人从起点到终点所需要的最短时间,说白了就是求两点间的最短路程。可以使用 Dijkstra 算法或者 Floyd 算法来解决此题。

题目描述

假设你住在一个完全由门构成的地方,你必须用一把钥匙来打开每个门,每个门都需要一定的时间才能打开(这个时间可能不同)。门之间没有限制,可以走过任意数量的门。所有的门都是相同的,只不过时间不同。你现在想知道你从一个门到另一个门所需要的时间是多少。你可以从一个门调整到另一个门。

输入

输入的第一行包含两个整数 n(1 <= n <= 10^3) 和 m(1 <= m <= 5*10^5),分别表示门的数量和门之间的路径数量。 接下来m行,每行包含三个整数 x、y、z,表示从x到y的门打开时间为z。所有门的编号从1到n。

输出

对于每个查询,输出从x到y的最短时间。

样例输入输出
输入样例:
3 3
1 2 1
2 3 2
1 3 3
输出样例:
1 3 3
解题思路

本题的解题思路有很多种,比较常见的是 Dijkstra 算法和 Floyd 算法。Dijkstra 算法又称为单源最短路径算法,解决的是给定一个节点作为起点,求其到图中其他节点的最短路径。Floyd 算法又称为多源最短路径算法,解决的是求任意两个节点之间的最短路径。两个算法都可以用来解决此题目。

Dijkstra 算法

Dijkstra 算法的基本思想是:每次从未确定的点中选择一个距离源点最近的点,然后以该点为中心进行扩展,更新其它点到源点的距离。按照这种方式不断扩展直到确定源点到所有点的最短路径。

具体实现方式是维护一个记录各个最短路径的数组 dist[],并采用优先队列来管理未确定的点。在每次迭代中,选取距离源点最近的点 u,并用 u 更新其他未确定的点到源点的距离。

void dijkstra(int s) {
    memset(vis, 0, sizeof(vis));
    memset(dist, INF, sizeof(dist));
    dist[s] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
    q.push(make_pair(dist[s], s));

    while (!q.empty()) {
        int x = q.top().second;
        q.pop();
        if (vis[x]) continue;
        vis[x] = 1;
        for (int i = head[x]; i != -1; i = e[i].next) {
            int y = e[i].to;
            int w = e[i].w;
            if (dist[y] > dist[x] + w) {
                dist[y] = dist[x] + w;
                if (!vis[y]) q.push(make_pair(dist[y], y));
            }
        }
    }
}
Floyd 算法

Floyd 算法的基本思想是:两个顶点之间的最短路径不仅仅取决于两个顶点之间的距离,也可能依赖于其他顶点的距离。所以 Floyd 算法采用动态规划的思想,对于每两对顶点之间计算其最短距离。

具体来说,定义一个 n × n 的二维数组 d 存储每两个顶点之间的最短路径,初始值为各个边的权值。接着对于每一个中转点 k,遍历每一对顶点 i 和 j,计算经过 k 点的路径长度是否比直接从 i 到 j 距离更短(即 d[i][j] > d[i][k] + d[k][j]),如果是则更新 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];
参考资料