最短路径

Dijkstra

迪杰斯特拉Dijkstra是求解单源最短路径的典型方法,指定源点s,求它到其余各个结点的最短路。思路是将所有结点分为两部分,一部分是已经确定最短路径的结点集P,剩余的结点集保存在Q中。初始时P中只有一个结点ss距离Q中各个结点的距离dis初始化为INT_MAX。每次在Q中找到距离源点s最近的结点u,将u加入到集合P中,这样即确定了su的最短距离dis[u];同时更新由sQ中剩余结点的最短距离,即对于Q中的特定结点v,比较原始的最短的路径值和经过了中间结点u的最短路径,取两者中的较小值:dis[v] = min(dis[v], dis[u]+matrix[u][v]),其中matrix为邻接矩阵。

如果需要记录到达某个结点的最短路径所经过的是哪些结点,那么需要维护path[i]这个数组,其中path[t]表示由源点s到达结点t最短路径中,t的前驱结点。如s->...->t0->tst的最短路径,那么path[t]=t0,再去求解到达t0的最短路径,直到找到源点sDijkstra本质是一种贪心算法,无法处理有负权重的问题。

最短路径
注:图片来源于https://www.jianshu.com/p/92e46d990d17


Floyd

动态规划。每次并入一个新顶点,看能否通过该顶点进行松弛。初始状态顶点i到顶点j的距离由邻接矩阵决定matrix[i][j],现在加入新的顶点,判断能否通过该顶点对matrix[i][j]进行松弛。如果matrix[i][k] + matrix[k][j] < matrix[i][j],说明顶点i先经过中间顶点k,然后再到顶点j比之间从顶点i到顶点j路程更短,松弛更新。依次并入剩余的所有顶点。


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

class Floyd {
public:
    //构建邻接矩阵
    Floyd(int n) : vertex_num(n), matrix(n, vector<int>(n, INT_MAX / 3)), path(n, vector<int>(n)) {
        for (int i = 0; i < n; ++i) {
            matrix[i][i] = 0;
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                //注意这里是j
                path[i][j] = j;
            }
        }
    }

    void setEdge(int start, int end, int w) {
        matrix[start][end] = w;        //有向图
    }

public:
    int vertex_num;
private:
    vector<vector<int>> matrix;
    //path[i][j]表示由顶点i到顶点j经过的中间顶点(如果没有中间顶点则为目标顶点j)。
    vector<vector<int>> path;

public:
    //返回最短路径值
    vector<vector<int>> floyd() {
        int cnt = vertex_num;
        //并入每个节点
        for (int k = 0; k < cnt; ++k) {
            for (int i = 0; i < cnt; ++i) {
                for (int j = 0; j < cnt; ++j) {
                    if (matrix[i][k] + matrix[k][j] < matrix[i][j]) {
                        matrix[i][j] = matrix[i][k] + matrix[k][j];
                        //路径设置为经过下标为k 的顶点
                        path[i][j] = path[i][k];
                    }
                }
            }
        }
        return matrix;
    }

    //显示由顶点src到目标点dst的最短路径
    void show_shortest_path(int src, int dst) {
        if (src > vertex_num || dst > vertex_num) {
            cout << "顶点超出范围:" << vertex_num - 1 << endl;
            return;
        }
        int k = path[src][dst];
        cout << src;
        while (k != dst) {
            cout << "->" << k;
            k = path[k][dst];
        }
        cout << "->" << dst << ": " << matrix[src][dst] << endl;
    }
};

class Dijkstra {
public:
    Dijkstra(int n) : nvert(n),matrix(n, vector<int>(n, INT_MAX)){
        for (int i = 0; i < n; ++i) {
            matrix[i][i] = 0;
        }
    }

    void setEdge(int start, int end, int w) {
        matrix[start][end] = w;        //有向图
    }

public:
    int nvert;
private:
    vector<vector<int>> matrix;
    vector<int> path;                    //path[i]记录的顶点i的前一个顶点,即path[i]->i
    int src;                                        //源顶点

public:
    //返回由顶点src到各个顶点的最短距离
    vector<int> dijkstra(int src) {
        if (src >= nvert) {
            cout << "src参数错误" << endl;
            return vector<int>{};
        }

        int n = nvert;
        vector<bool> visited(n, false);
        visited[src] = true;                //初始时P集合只有src这个节点
        vector<int> dist(matrix[src].begin(), matrix[src].end());
        path.resize(n, src);
        //循环n-1次就能将Q遍历空
        for (int time = 1; time < n; ++time) {
            int min = INT_MAX;
            int u = src;
            //寻找Q中最近的结点,保存到u中
            for (int i = 0; i < n; ++i) {
                if (!visited[i] && dist[i] < min) {
                    min = dist[i];
                    u = i;
                }
            }
            if (u == src) continue;

            visited[u] = true;                            //将结点u加入到P集合
            for (int v = 0; v < n; ++v) {            //更新u的所有出边
                 //小心加法溢出
                if (!visited[v] && matrix[u][v] != INT_MAX && dist[u] + matrix[u][v] < dist[v]) {
                    dist[v] = dist[u] + matrix[u][v];
                    path[v] = u;
                }
            }
        }
        this->src = src;
        return dist;
    }

    //输出顶点src到dst最短路线
    vector<int> show_shortest_path(int dst) {
        if (dst >= nvert) {
            cout << "dst参数错误" << endl;
            return vector<int>{};
        }
        vector<int> shortest_path;
        while (dst != src)
        {
            shortest_path.push_back(dst);
            dst = path[dst];
        }
        shortest_path.push_back(src);
        for (auto iter = shortest_path.rbegin(); iter != shortest_path.rend(); ++iter)
            cout << "->" << *iter;

        reverse(shortest_path.begin(), shortest_path.end());
        return shortest_path;
    }
};

int main() {
    int n;
    cin >> n;
    Floyd floyd(n);
    Dijkstra dijkstra(n);

    int edge;
    cin >> edge;
    while (edge--)
    {
        int s, e, w;
        cin >> s >> e >> w;
        floyd.setEdge(s, e, w);
        dijkstra.setEdge(s, e, w);
    }

    vector<vector<int>> res = floyd.floyd();
    for (int i = 0; i < floyd.vertex_num; ++i)
        floyd.show_shortest_path(0, i);

    vector<int> dist = dijkstra.dijkstra(0);
    dijkstra.show_shortest_path(5);
}

输入:

6
8
0 2 10
0 4 30
0 5 100
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60

输出:

0->0: 0
0->1: 715827882
0->2: 10
0->4->3: 50
0->4: 30
0->4->3->5: 60
->0->4->3->5

相关文章: