利用Dijkstra算法实现记录每个结点的所有最短路径

最近在做PAT时发现图论的一些题目需要对多条最短路径进行筛选,一个直接的解决办法是在发现最短路径的时候就进行判断,选出是否更换路径;另一个通用的方法是先把所有的最短路径记录下来,然后逐个判断。前者具有一定的难度并且不好排查BUG,因此我设计了一种基于Dijkstra的记录所有最短路的简捷算法,用于解决此类题目。

我们知道,Dijkstra是解决单源最短路问题的,并且最基本的算法仅能求出最短路的长度,而不能输出路径,本文基于Dinjkstra进行改进,使之能记录源点到任意点的所有最短路径。

使用vector<int>来记录一条路径,因为每个结点可能有多条最短路径,因此把这些路径都装在一个vector中,因此可以用一个vector<vector<int> >来表示一个结点的所有最短路径,把所有结点的最短路径都存放起来,又需要一个vector容器,因此所有结点的所有最短路径的集合可以用vector<vector<vector<int> > >来表示。

约定:结点编号为0到N-1,源点为0,到每个点的最短距离存储在数组minD[N]中。

在Dijkstra算法初始化时,找出所有源点的邻接点w并且把相应的最短距离minD[w]更新,同时初始化这些点w的第一条最短路径0->w(实现方法为分别push_back 0和w)。接下来将会找到一个到源点最短的点v,并且把v并入集合,对v的所有未访问的邻接点,如果到达w的路径(0->...->w)在包含v之后(0->...->v->w)变短,则删除w之前所有的最短路径,并且更新为到v的所有最短路径加上w点(注意对每个到v的最短路径都要这样处理);如果到达w的路径在包含v之后长度不变,说明发现了一条新的最短路径,在w原来最短路径容器的基础上再压入一个新的最短路径,这条路径为所有到v的最短路径加上w点。

经过这样的运算,就可以得到所有结点的所有最短路径了,下面以一个实例对算法进行测试,并且附上源代码。

题目:求下图的源点0到所有结点的最短路径。


输入:

5 8
2 4 1
0 1 3
0 2 6
1 3 2
1 4 1
3 4 1
3 2 1
0 4 4

输出:



源码为:

#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;

#define MAX 1001
#define INF 99999999

int G[MAX][MAX];
int minD[MAX];
int minDist;
int finalSet[MAX];

int main()
{
    int N,M;
    int v1,v2;
    int len;
    cin >> N >> M;
    for(int i = 0; i < N; i++){
        finalSet[i] = 0;
        minD[i] = INF;
        for(int j = 0; j < N; j++)
            G[i][j] = INF;
    }
    for(int i = 0; i < M; i++){
        scanf("%d%d%d",&v1,&v2,&len);
        G[v1][v2] = G[v2][v1] = len;
    }

    vector<vector<vector<int> > > nodes(N);

    // 设0为源点,计算从0到所有点的所有最短路径
    finalSet[0] = 1;
    minD[0] = 0;
    // 首先把所有源点邻接点的最短距离初始化为源点到这些点的距离
    for(int i = 1; i < N; i++){
        if(G[0][i] != INF) {
            minD[i] = G[0][i];
            vector<vector<int> > minPaths;
            minPaths.clear();
            vector<int> pathList;
            pathList.clear();
            pathList.push_back(0);
            pathList.push_back(i);
            minPaths.push_back(pathList);
            nodes[i] = minPaths;
        }
    }

    // 从所有minD中找出最小的,并入集合S,重复N-1次(源点已经加入集合)
    for(int i = 1; i < N; i++){
        minDist = INF;
        int v = -1; // 记录到源点记录最小的结点
        for(int w = 1; w < N; w++){
            if(!finalSet[w] && minDist > minD[w]){
                minDist = minD[w];
                v = w;
            }
        }
        if(v == -1) break; // v = -1说明找不到点了,当图不连通时才会出现这种情况
        // 已经找到了到源点最近的点v,将其并入集合,并且考虑原来的最短距离0->...->W在加入了v之后有没有可能变短
        // 如果变短了,就更新为0->...>v->W
        finalSet[v] = 1;
        for(int w = 1; w < N; w++){
            if(!finalSet[w]){
                int newD = minDist + G[v][w];
                if(newD < minD[w]){
                    minD[w] = newD;
                    vector<vector<int> > minPathsV = nodes[v];
                    vector<int> pathList;
                    nodes[w].clear();
                    for(int index = 0; index < minPathsV.size(); index++){
                        pathList = minPathsV[index];
                        pathList.push_back(w);
                        nodes[w].push_back(pathList);
                    }

                }else if(newD == minD[w]){

                    vector<vector<int> > minPathsV = nodes[v];
                    vector<int> pathList;
                    for(int index = 0; index < minPathsV.size(); index++){
                        pathList = minPathsV[index];
                        pathList.push_back(w);
                        nodes[w].push_back(pathList);
                    }

                }
            }
        }
    }
    for(int i = 1; i < N; i++){
        cout << "------------" << endl;
        cout << "0 to "<< i << ":" << endl;
        cout << "The miniest distance:" << endl << minD[i] << endl;
        cout << "The possible paths:" << endl;
        vector<vector<int> >minPaths = nodes[i];
        int size = minPaths.size();
        vector<int> pathList;
        for(int j = 0; j < size; j++){
            pathList = minPaths[j];
            int pathSize = pathList.size();
            for(int k = 0; k < pathSize - 1; k++){
                cout << pathList[k] << "->";
            }
            cout << pathList[pathSize - 1] << endl;
        }

    }

    return 0;
}



posted on 2015-05-29 13:16  张大大123  阅读(1387)  评论(0编辑  收藏  举报

导航