[算法] 最短路径条数问题

问题

给定如图所示的无向连通图,假定图中所有边的权值均为1,显然,从源点A到终点T的最短路径有多条,求不同的最短路径数目。
这里写图片描述

分析

权值相同的最短路径问题,则单源点 D i j k s t r a 算法退化为BFS广度优先算法, 使用 D i j k s t r a 算法只能求的到各个节点的最短路径,但不能求得最短路径的条数。假定起点为0,终点为N,数组 s t e p [ 0... N 1 ] s t e p [ i ] 代表到第 i 个节点的最短路径长度,数组 p a t h N u m [ 0... N 1 ] p a t h N u m [ i ] 代表到第 i 个节点的最短路径条数,首先:

  • s t e p [ 0... N 1 ] 初始化为0
  • p a t h N u m [ 0... N 1 ] 初始化为0
  • p a t h N u m [ 0 ] 为1,即起点到起点的路径数量为1

从当前节点 i 扩展到邻接节点 j 时:

  • s t e p [ j ] 为0,则表示从未到达过节点 j ,则:

    • s t e p [ j ] = s t e p [ i ] + 1 p a t h N u m [ j ] = p a t h N u m [ i ]
  • s t e p [ j ] == s t e p [ i ] + 1 ,表示已经有从其他节点到达过节点 j ,并且路径长度和从节点 i 到节点 j 的路径长度相等,则已有到达 j 的路径数加上经过 i 到达 j 的路径数为当前从源点到达节点 j 的路径数量:

    • p a t h N u m [ j ] + = p a t h N u m [ i ]
  • s t e p [ j ] > s t e p [ i ] + 1 ,则已有的经过其他节点到达节点 j 的路径长度要比经过节点 i 到达节点 j 路径长度要长,则需要更新到达节点 j 的路径长度和路径数量,即:

    • s t e p [ j ] = s t e p [ i ] + 1 p a t h N u m [ j ] = p a t h N u m [ i ]
  • 当扩展到节点 N 时,终止算法

代码实现

借助广度优先搜索的思想遍历所有节点,具体实现如CalPathNum.hpp所示:

#ifndef CalPathNum_hpp
#define CalPathNum_hpp

#include <stdio.h>
#include <string.h>
#include <queue>

const int nodeCnt = 16;
int calculatePathNum(int graph[nodeCnt][nodeCnt]) {
    int step[nodeCnt]; // step[i]表示到第i个节点的步数
    int pathNum[nodeCnt]; // pathNum[i]表示到第i个节点的最短路径条数
    memset(step, 0, sizeof(int)*nodeCnt);
    memset(pathNum, 0, sizeof(int)*nodeCnt);
    pathNum[0] = 1; // 起点到起点的路径数量为1
    std::queue<int> q; // 广度优先搜索中保存当前到达的节点
    q.push(0); // 首先到达起点
    int from, i, s;
    while (!q.empty()) {
        from = q.front();
        q.pop();
        s = step[from] + 1; // 和from节点相邻的节点的路径长度
        for (i = 1; i < nodeCnt; i++) { // 0是起点,不需要遍历
            if (graph[from][i] == 1) { // 从from到i是连通的
                if (step[i] == 0 || step[i] > s) { // i节点从未到达过或发现更快的路径
                    step[i] = s;
                    pathNum[i] = pathNum[from];
                    q.push(i); // 将和from相邻的节点入队,相当于广度优先搜索中form节点的下一层节点入队
                } else if(s == step[i]){ // 发现相同长度的路径
                    pathNum[i] += pathNum[from];
                }
            }
        }
    }
    return pathNum[nodeCnt-1];
}

#endif /* CalPathNum_hpp */

测试代码如main.cpp:

#include "CalPathNum.hpp"

int main(int argc, const char * argv[]) {
    // 测试最短路径条数
    int graph[16][16];
    memset(graph, 0, sizeof(int) * 16 * 16);
    graph[0][1] = graph[0][4] = 1;
    graph[1][5] = graph[1][0] = graph[1][2] = 1;
    graph[2][1] = graph[2][6] = graph[2][3] = 1;
    graph[3][2] = graph[3][7] = 1;
    graph[4][0] = graph[4][5] = 1;
    graph[5][1] = graph[5][4] = graph[5][6] = graph[5][9] = 1;
    graph[6][2] = graph[6][5] = graph[6][7] = graph[6][10] = 1;
    graph[7][3] = graph[7][6] = 1;
    graph[8][9] = graph[8][12] = 1;
    graph[9][8] = graph[9][13] = graph[9][10] = 1;
    graph[10][9] = graph[10][14] = graph[10][11] = 1;
    graph[11][10] = graph[11][15] = 1;
    graph[12][8] = graph[12][13] = 1;
    graph[13][9] = graph[13][12] = graph[13][14] = 1;
    graph[14][10] = graph[14][13] = graph[14][15] = 1;
    graph[15][11] = graph[15][14] = 1;
    printf("%d\n", calculatePathNum(graph));

    return 0;
}

该算法事实上是广度优先搜索的一个应用

猜你喜欢

转载自blog.csdn.net/zkp_java/article/details/80710752