迪杰斯特拉路径保存

第一种方式:用pre[ ]数组表示从起点s到顶点v的最短路径上v的前一个顶点的编号。

如果d[u] + G[u][v] < d[v],说明以u作为中介点可以使d[v]更优,此时需要令v的前驱节点为u,并且即便原先的pre[v]存放了若干个节点都应该清空,然后再添加u,因为正是因为这个中介点才使得到达v这个节点的路径变短,所有之前到达v的所有节点都需要清除,然后将u节点添加进去。
如果d[u] + G[u][v] == d[v],说明以u为终结点可以找到一条距离相同的路径,此时不需要清空pre[v],直接将结点u添加进去。

if(d[u] + G[u][v] < d[v])
{
     
    d[v] = d[u] + G[u][v];
    pre[v].clear();
    pre[v].push_back(u);
}
else if(d[u] + G[u][v] == d[v])
{
     
    pre[v].push_back(u);
}

迪杰斯特拉路径保存_第1张图片
如图,我们想要输出从V1到V4的最短路径。
首先要从pre[4]得到V3,再从pre[3]得到V2,一直继续。

输出最优路径:

void DFS(int s, int v)  //s为起点,v为当前结点(从终点递归)
{
     
    
    //递归边界
    if(s == v)      //如果已经到达起点s,就输出起点并返回
    {
     
        printf("%d ", s);
        return;
    }   
    //递归边界
    DFS(s, pre[v]); //递归访问v的前驱
    printf("%d ", v);   //从最深处return回来后,输出每一层的顶点号
}
const int maxn = 1000;
const int INF = 100000;
int G[maxn][maxn], d[maxn];
int pre[maxn];  //表示最短路径中v的前一个顶点
bool vis[maxn] = {
     false};
int n;

void Dijkstra(int s)
{
     
    fill(d, d + maxn, INF);
    d[s] = 0;
    for(int i = 0; i < n; i++)
    {
     
        int u = - 1, MIN = INF;
        for(int j = 0; j < n; j++)
        {
     
            if(vis[j] == false && d[j] < MIN)
            {
     
                u = j;
                MIN = d[j];
            }
        }
        if(u == -1) return;
        vis[u] = true;
        for(int v = 0; v < n; v++)
        {
     
            if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v])
            {
     
                d[v] = d[u] + G[u][v];
                pre[v] = u;     //记录v的前驱是u
            }
        }
    }
}


void DFS(int s, int v)  //s为起点,v为当前结点(从终点递归)
{
     
    
    //递归边界
    if(s == v)      //如果已经到达起点s,就输出起点并返回
    {
     
        printf("%d ", s);
        return;
    }   
    //递归边界
    DFS(s, pre[v]); //递归访问v的前驱
    printf("%d ", v);   //从最深处return回来后,输出每一层的顶点号
}

第二种方式:Dijkstra+DFS,先用Dijkstra求出所有路径,然后从这些路径中选出一条第二标尺最优的路径。
思路:用一个vector pre[maxn]记录前驱。pre[v]是一个多维数组,如果v的前驱是1,2,3,那么pre[v][0]~pre[v][3]就是1,2,3。

int optValue;       //第二标尺最优值
vector<int> pre[maxn];  //前驱
vector<int> path, tempPath; //最优路径和临时路径

void DFS(int v)       //v为当前访问结点
{
     
    if(v == st)         //如果到达了叶子结点st(即路径起点)
    {
     
        tempPath.push_back(v);  //将起点st加入临时路径tempPath的最后面
        int value;              //存放临时路径tempPath的第二标尺的值
        计算路径tempPath上的value值
        if(value优于optValue)
        {
     
            optValue = value;   //更新第二标尺最优值与最优路径
            path = tempPath;
        }
        tempPath.pop_back();    //将刚加入的结点删除
        return;
    }
    
    tempPath.push_back(v);      //将当前访问节点加入临时路径tempPath的最后面
    for(int i = 0; i < pre[v].size(); i++)
    {
     
        DFS(pre[v][i]);         //结点v的前驱为pre[v][i],递归
    }
    tempPath.pop_back();        //遍历完所有前驱结点,将当前结点删除。
}

输出:需要注意的是,存放在tempPath中的路径结点是逆序的,因此需要倒着访问(计算边权和时可以正序)。以计算最短路径的边权之和为例。

//边权之和
int vaulue = 0
for(int i = tempPath.size() - 1; i >= 0; i--)   //倒着访问结点
{
     
    //当前结点id,下一个结点idNext
    int id = tempPath[i], idNext = tempPath[i - 1];
    value += V[id][idNext];     //value增加边id->idNext的边权
}

//点权之和
int value = 0;
for(int i = tempPath.size() - 1; i >= 0; i--)
{
     
    int id = tempPath[i];   //当前结点
    value += W[id];     //value增加点id的点权
}

你可能感兴趣的:(PAT)