📜  补图中的最短路径(1)

📅  最后修改于: 2023-12-03 14:57:19.497000             🧑  作者: Mango

补图中的最短路径

在图论中,最短路径问题是指在加权图中找到两个顶点之间的最短路径。在补图中,最短路径问题则是在一个不完全的图中找到两个不相邻的节点之间最短的一条路径。

算法介绍

常用的最短路径算法有Dijkstra算法和Floyd-Warshall算法。在补图中,由于图不完全,常规的Dijkstra算法和Floyd-Warshall算法并不能直接应用。因此,我们需要定制一些算法来解决这个问题。

定义

设补图为G=(V,E),其中V是补图中所有的点,E是所有点对之间的差集,即所有无向边。

算法步骤

预处理

补图中的任意两个非相邻节点不连通,所以它们之间的路径长度为无穷大。因此,我们需要先对所有不相邻的节点之间的距离进行初始化处理。这里我们可以使用Floyd算法来计算所有不相邻节点之间的距离。

寻找最短路径

在预处理之后,我们就可以使用Dijkstra算法来在补图中寻找最短路径了。不同于常规的Dijkstra算法,这里需要忽略所有不可达的节点。

  1. 初始化距离数组,将所有起点能直接到达的节点的距离初始化为它们之间的边权值。

  2. 将起点加入到最小堆中。

  3. 循环以下步骤,直到最小堆为空:

    • 弹出堆顶元素,将其标记为已访问。

    • 对该节点能够到达的所有未访问节点进行松弛操作。若该节点能够直接到达该节点,则更新该节点到起点的距离,并将其加入到最小堆中。

  4. 最终得到起点到各个可达节点的最短路径。

算法分析

预处理的时间复杂度为O(n^3),Dijkstra算法的时间复杂度为O(nlogn + m),其中n表示节点数量,m表示边的数量。因为补图的边数为n(n-1)/2-m,所以在补图中,Dijkstra算法的时间复杂度为O(nlogn + n^2)。总的时间复杂度为O(n^3)。

代码实现
// 预处理
void floyd(int n, vector<vector<int>> &dist) {
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            if (i == k || dist[i][k] == INF) continue;
            for (int j = 0; j < n; j++) {
                if (j == k || dist[k][j] == INF) continue;
                if (dist[i][j] == INF || dist[i][j] > dist[i][k] + dist[k][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }
}

// Dijkstra算法
void dijkstra(int n, int s, vector<vector<int>> &dist, vector<bool> &visited, vector<int> &pre) {
    // 最小堆
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;

    // 初始化起点的距离
    for (int i = 0; i < n; i++) {
        if (i == s || dist[s][i] == INF) continue;
        q.push({dist[s][i], i});
    }

    while (!q.empty()) {
        // 弹出堆顶元素
        int node = q.top().second;
        int distance = q.top().first;
        q.pop();
        if (visited[node]) continue;
        visited[node] = true;
        pre[node] = s;

        // 对能到达的所有节点进行松弛操作
        for (int i = 0; i < n; i++) {
            if (visited[i] || i == node) continue;
            if (dist[node][i] == INF) continue;
            if (dist[s][i] == INF || dist[s][i] > distance + dist[node][i]) {
                dist[s][i] = distance + dist[node][i];
                q.push({dist[s][i], i});
                pre[i] = node;
            }
        }
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> edge(n, vector<int>(n, INF));
    vector<bool> visited(n, false);
    vector<int> pre(n, -1);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        u--, v--;
        edge[u][v] = min(edge[u][v], w);
        edge[v][u] = min(edge[v][u], w);
    }
    floyd(n, edge);
    for (int i = 0; i < n; i++) {
        if (visited[i]) continue;
        dijkstra(n, i, edge, visited, pre);
    }
    return 0;
}

代码中使用了邻接矩阵来存储补图,使用Floyd算法来处理所有不相邻节点之间的距离,使用Dijkstra算法来进行最短路径的寻找。