Loading

最短路三种算法详解

最短路

最短路问题即,给你一张图,让你求出图中两点的最短距离。

这篇文章会讲解 \(Dijkstra\)\(Spfa\)\(Floyd\) 三种算法,让您透彻理解最短路!

Dijkstra

朴素版

题目:

image

\(Dijkstra\) 通常是用来解决图中一个定点到其余点的最短距离,基本思路是:从中心向外扩展,直到扩展到终点为止。

我们用 \(dist[u]\) 来表示从源点 \(s\)\(u\) 的最短距离。

还需要维护一个状态数组 \(st\),用于在更新最短距离时,判断当前的点的最短距离是否需要更新(是否已经确定)。

在每一次扩展的过程中,我们要先找到当前未确定的点的集合中找到一个距离最小的点,也就是:

int t = -1; // 距离最短的点的编号
for (int j = 1; j <= n; ++j) 
    if (!st[j] && (t == -1 || dist[j] < dist[t])) // 判断是否符合条件
        t = j;

那么我们就已经确定这个点了,把它的状态更新一下:

st[t] = true;

然后用这个点更新所有未确定点的最短距离

for (int j = 1; j <= n; ++j) 
    if (!st[i]) // 这句话其实可以不用加,因为我们dijkstra算法的性质已经确定后面确定的点不会再比现在的小了
        dist[j] = min(dist[j], dist[t] + g[t][j]);

完整代码:

#include <iostream>
#include <cstring>
#define N 510
using namespace std;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0; // 源点到源点的距离是0
    for (int i = 1; i <= n; ++i) {
        int t = -1;
        for (int j = 1; j <= n; ++j) 
            if (!st[j] && (t == -1 || dist[j] < dist[t])) 
                t = j;
        st[t] = true;
        for (int j = 1; j <= n; ++j) dist[j] = min(dist[j], dist[t] + g[t][j]);
    }
    if (dist[n] == 0x3f3f3f3f) return -1; // 不存在最短路径
    return dist[n];
}
int main() {
    memset(g, 0x3f, sizeof g);
    cin >> n >> m;
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c); // 防止重边的情况
    }
    cout << dijkstra() << '\n';
    return 0;
}

此代码时间复杂度为 \(O(n^2)\)

优化

我们发现,朴素 \(Dijkstra\) 主要就耗时在找 \(t\) 上了;在一组数中找到一个最小值,我们自然可以想到用优先队列。

于是我们的代码就可以优化成这样了:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define N 200000
typedef pair<int, int> PII; // 存储距离和节点编号
int n, m;
int h[N], e[N], w[N], ne[N], idx; // 因为是点数较多,所以用邻接表存图
int dist[N];
bool st[N];
void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; }
int dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, 1});
    while (q.size()) {
        auto t = q.top();
        q.pop();
        int ver = t.second;
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = h[ver]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i]) {
                dist[j] = dist[ver] + w[i];
                q.push({dist[j], j});
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main() {
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    cout << dijkstra() << '\n';
    return 0;
}

剩下的咕咕,明天再写。都咕两个月了还没补

posted @ 2023-08-26 20:28  popcoount  阅读(34)  评论(1编辑  收藏  举报