Figure - the shortest path -Dijkstra its variants

Shortest Path

The shortest path problem:

FIG Given any G(V,E)and starting point S, the end point T, how to find the shortest path from S to T.

Common method of solving the shortest path there

  • Dijkstra's algorithm
  • Bellman-Ford algorithm
  • SPFA algorithm
  • Floyd algorithm

Here mainly Dijkstra algorithm and its variants are summarized.

Dijkstra's algorithm

ALGORITHM

Dijkstra's algorithm is used to resolve the single-source shortest path problem , i.e., given a graph G, and the starting point s, by the shortest distance S algorithm every other vertex.

Dijkstra algorithm is the basic idea :

For Figure G (V, E) disposed Set S, stored vertices have been visited, and then select the minimum and the shortest distance between each starting point s of a vertex (denoted u) from the set of VS, the access to the collection and S. After that, make vertex u as an intermediary point, the starting point s to optimize the shortest distance from all vertices u can reach. This operation is performed n (n is the number of vertices) until set S already contains all the vertices.

Detailed strategy:

First set the set S stored vertices have been accessed, then n times (number of vertices) following two steps

  1. From each set of V - selecting a starting point s and vertex shortest distance (referred to as U) S, the access to the collection and S
  2. Order vertex u is an intermediary point between the minimum distance between all vertices reachable from u v

Implementation

In the implementation process, there are two main issues to consider:

  • To achieve the set S
  • S arrival starting vertex V I shortest distance (0 <= i <= n -1) is
  1. A set S can VIS bool array [] is achieved, i.e., when vis [i] == true time of vertices V I has been accessed
  2. D array so int [] represents the origin to the vertices of V I shortest distance, in addition to the initial starting point s when the d [s] = other than 0, the remaining vertices of a large number Ode

Pseudo-code as follows:

void Dijkstra(G, d[], s) {
    初始化;
    for(循环n次) {
       u = 使 d[u] 最小的,还未访问的顶点标号;
       记录 q 被访问;
       for(从u出发能够到达的所有顶点v){
         if(v未被访问 && 以u为中介点使得s到v的最短距离d[v]更优) {
           优化d[v];
         }
       }
    }
}

Adjacency matrix version

const int MAXV = 1000;  // 最大顶点数
const int INF = 1e9;        // INF很大的数字

int n, G[MAXV][MAXV];       // n 为顶点数,MAXV为最大顶点数
int d[MAXV];                        // 起点到达各点的最短路径长度
bool vis[MAXV] = {false};   // 是否被访问过

// s 为起点
void Dijkstra(int s) {
        fill(d, d+MAXV, INF);   // 初始化距离为最大值
    d[s] = 0;
    for(int i = 0; i < n; i++) {    // 重复 n 次
      int u = -1, MIN = INF;
      
      // 遍历找到未访问顶点中 d[] 最小的
      for(int j = 0; j < n; j++) {
        if(vis[j] == false && d[j] < MIN) {
          u = j;
          MIN = d[j];
        }
      }
      
      if(u == -1) return;   // 找不到小于 INF 的 d[u],说明剩下的顶点和起点 s 不连通
      vis[u] = true;    // 标记 u 为已访问
      for(int v = 0; v < n; v++) {
        // 如果v未访问 && u 能到达 v && 以 u 为中介点可以使 d[v] 更优
        if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]) {
          d[v] = d[u] + G[u][v];
        }
      }
    }  
}

Time complexity: the outer loop O (V), the inner loop O (V), v enumeration needs O (V), the overall complexity is O (V * (V + V)) = O (V 2 ).

Adjacency list version

struct Node {
  int v, dis;   // v 为目标顶点,dis 为边权
};
vector<Node> Adj[MAXV]; //图G,Adj[u]存放从顶点 v 出发可以到达的所有顶点
int n;  // n为顶点数
int d[MAXV];    // 起点到达各点的最短路径长度
bool vis[MAXV] = {false};

void Dijkstra(int s) {// s 为起点
  fill(d, d+MAXV, INF);
  d[s] = 0; // 到自身为 0
  for(int i = 0; i < n; i++) {  // 循环 n 次
    int u = -1, MIN = INF;
    
    // 遍历找到未访问顶点中 d[] 最小的
      for(int j = 0; j < n; j++) {
        if(vis[j] == false && d[j] < MIN) {
          u = j;
          MIN = d[j];
        }
      }
      
      if(u == -1) return;   // 找不到小于 INF 的 d[u],说明剩下的顶点和起点 s 不连通
      vis[u] = true;    // 标记 u 为已访问
    
    // 和邻接矩阵不同
    for(int j=0; j < Adj[u][j].size(); j++) {
      int v = Adj[u][j].v;  // 获得u能直接到达的顶点
      // v 未访问 && 以 u 为中介点到达 v 比 d[v] 更短
      if(vis[v] == false && d[u] + Adj[u][j].dis < d[v]) {
        // 更新
        d[v] = d[u] + Adj[u][j].dis;    
      }
    } 
  }
}

When a subject is undirected edge (bidirectional side) when the edges have not, just as the two sides make no point in opposite directed edges can.

Shortest Path

We have not at this time when it comes to how to record the shortest path, we return to pseudo-code, there is a step

    for(从u出发能够到达的所有顶点v){
         if(v未被访问 && 以u为中介点使得s到v的最短距离d[v]更优) {
           优化d[v];
         }
       }

We it records this information at this time down, is provided an pre[]array, so that pre[v]indicates the number of previous vertices (precursor node) on the shortest path from the starting point s to the vertex v, so that, when the pseudo code condition is satisfied, u put assigned pre [v], ultimately recorded.

Pseudocode becomes:

for(从u出发能够到达的所有顶点v){
         if(v未被访问 && 以u为中介点使得s到v的最短距离d[v]更优) {
           优化d[v];
           令 v 的前驱为 u
         }
       }

To adjacency matrix as an example:

const int MAXV = 1000;  // 最大顶点数
const int INF = 1e9;        // INF很大的数字

int n, G[MAXV][MAXV];       // n 为顶点数,MAXV为最大顶点数
int d[MAXV];                        // 起点到达各点的最短路径长度
int pre[MAXV];  // 记录最短路径
bool vis[MAXV] = {false};   // 是否被访问过

// s 为起点
void Dijkstra(int s) {
        fill(d, d+MAXV, INF);   // 初始化距离为最大值
    d[s] = 0;
    for(int i = 0; i < n; i++) {    // 重复 n 次
      int u = -1, MIN = INF;
      
      // 遍历找到未访问顶点中 d[] 最小的
      for(int j = 0; j < n; j++) {
        if(vis[j] == false && d[j] < MIN) {
          u = j;
          MIN = d[j];
        }
      }
      
      if(u == -1) return;   // 找不到小于 INF 的 d[u],说明剩下的顶点和起点 s 不连通
      vis[u] = true;    // 标记 u 为已访问
      for(int v = 0; v < n; v++) {
        // 如果v未访问 && u 能到达 v && 以 u 为中介点可以使 d[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) {
  if(v == s) {  // 已经递归到起点 s
    printf("%d\n", s);
    return;
  }
  DFS(s, pre[v]);           // 递归访问 v 的前驱顶点 pre[v]
  printf("%d\n", v);    // 从最深处 return 回来之后输出每一层的结点号
}

Multiple shortest path

At this point we have learned and method for finding the shortest path Dijkstra, but usually more than one shortest path.

This then has the shortest path across two or more can be achieved, will give the title second scale (a first distance scale), required to select a second scale optimal shortest paths in all path.

There are usually three ways:

  1. Each edge to add a right side (such as cost)
  2. Each point increase to a point right
  3. Directly asked how many shortest paths

For these three questions, only need to add a new array to store the right edge or point right or shortest path number, and then modify the optimization of d[v]the steps can be.

For these three questions, namely the solution:

  1. Add the right side. To add right side represents the cost, for example, with cost[u][v]representatives from spent u-> v, increasing an array c[], so that least cost from the starting point s to vertex u is c [u], initialization only c[s] = 0, the rest are for the INF (distance maximum). Then d[u] + G[u][v] < d[v], the update d[v]and c[v], when d[u] + G[u][v] == d[v]and c[u]+cost[u][v] < c[v]updating c[v].
for(int v = 0; v < n; v++) {
        // 如果v未访问 && u 能到达 v && 以 u 为中介点可以使 d[v] 更优
        if(vis[v] == false && G[u][v] != INF) {
          if(d[u] + G[u][v] < d[v]) {
            d[v] = d[u] + G[u][v];
            c[v] = c[u] + cost[u][v];
          }else if(d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]) {
            c[v] = c[u] + cost[u][v];
          }
        }
      }
  1. Ibid., Is replaced by an array of weights.
  2. Only need to add an array num[], so that the number reaches the shortest path from vertex u s as the starting point num[u], the time of initialization, num[s] = 1the remainder is 0. When d[u] + G[u][v] < d[v]the time, let num [v] inherit num [u]. When d[u] + G[u][v] == d[v]the num [u] was added num [v] on. code show as below:
for(int v = 0; v < n; v++) {
        // 如果v未访问 && u 能到达 v && 以 u 为中介点可以使 d[v] 更优
        if(vis[v] == false && G[u][v] != INF) {
          if(d[u] + G[u][v] < d[v]) {
            d[v] = d[u] + G[u][v];
            num[v] = num[u];
          }else if(d[u] + G[u][v] == d[v] && c[u] + cost[u][v] < c[v]) {
            num[v] += num[u]; //最短距离相同时累加 num
          }
        }
      }

To consolidate the shortest path through two questions:

1003 PTA , this topic is very worth doing, Dijkstra's algorithm to consider three kinds of variants, is well familiar with Dijkstra.

Dijkstra+DFS

With the above subject Dijkstra generally do, and the case where a plurality of scale is prone to error, a more general description, the method for templating --Dijkstra + DFS here.

In the algorithm pre array always maintained optimal path, which is a clear need in the course of the Dijkstra algorithm to determine when to update the predecessor node for each node v pre [v]. More simple method is: first record all the Dijkstra shortest path algorithm (considering only the distance), and then select an optimal path from the second scale in these paths .

  1. Use Dijkstra shortest path algorithm to record all

The need to record all the shortest paths, each node will present a plurality of predecessor nodes, this can be used vector<int> pre[MAXV]to store predecessor node. For each node v is, pre [v] is a variable length array vector, which is used to store all of the precursor to produce the shortest path node of the node v.

Next consider the change in pre array update d [v] in the process. First, if d[u]+G[u][v] < d[v], as described in intermediate points can u D [v] More preferably, the precursor at this time so that the node is v u, and even before the pre [v] has been stored in a number of nodes, where it should be emptied, then add u, because previously saved at this time is not the optimal path.

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

So we can write the code complete as follows:

vector<int> pre[MAXV];
// s 为起点
void Dijkstra(int s) {
        fill(d, d+MAXV, INF);   // 初始化距离为最大值
    d[s] = 0;
    for(int i = 0; i < n; i++) {    // 重复 n 次
      int u = -1, MIN = INF;
      
      // 遍历找到未访问顶点中 d[] 最小的
      for(int j = 0; j < n; j++) {
        if(vis[j] == false && d[j] < MIN) {
          u = j;
          MIN = d[j];
        }
      }
      
      if(u == -1) return;   // 找不到小于 INF 的 d[u],说明剩下的顶点和起点 s 不连通
      vis[u] = true;    // 标记 u 为已访问
      for(int v = 0; v < n; v++) {
        // 如果v未访问 && u 能到达 v && 以 u 为中介点可以使 d[v] 更优
        if(vis[v] == false && G[u][v] != INF) {
          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. Through all the shortest path to find the best path to a second scale.

As the precursor node of each node may have multiple, traversing process will form a recursive tree, we can use either the DFS find the optimal path. When the tree is traversed, each will have a leaf node is reached full shortest path, each path to obtain, can be calculated value of the second scale, and make optimal current value of the second scale compared If better than the optimal value, the optimal value is updated, and the current optimal path covered by this path.

We consider how this recursive function of the implementation.

  • As a second scale global variable optimum value optValue
  • Recording the optimal path path array (used to store vector)
  • Temporary recording DFS path traversed temppath the leaf node (using vector storage)

Then consider two recursive function configuration: recursive and recursive boundary, if the access node is a leaf node (starting ST), then the recursion reaches the boundary description, a case tempPath storage path, the second scale is obtained optValue value and compare.

TempPath generated in a recursive process. As long as accessing the current node v is added to the rearmost v temppath then traverse pre [v] recursively, other pre [v] of all the nodes is completed before traversing the rearmost v temppath pop.

int optValue;
vector<int> pre[MAXV];
vector<int> tempPath, path;
int st; // 出发结点

void DFS(int v) {
  if(st == v) {
    // 递归到了出发结点
    tempPath.push(v);
    int value;
    计算路径 tempPath 上的value值
    if(value优于optValue) {
      optValue = value;
      path = tempPath;
    }
    tempPath.pop_back();    // 把刚刚加入的结点弹出来哦
    return;
  }else {
    tempPath.push_back(v);  // 把当前访问结点加入临时路径 tempPath 的最后面
    for(int i = 0; i < pre[v].size(); i++) {
      DFS(pre[v][i]);
    }
    tempPath.pop_back();
  }
}

When we encounter a point or right side right, we just need to modify the process of calculating the value of value.

However, note that, in the storage nodes in the path tempPath reverse order, so that access node needs to be backwards.

// 边权之和
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--) {
  int id = tempPath[i], idNext = tempPath[i-1];
  value += V[id][nextId];
}

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

If you need to record the number of the shortest path, the DFS may also be in the process, each leaf node reaching time enabling the global variable is incremented.

1030 PTA , this problem using the idea of Dijkstra + DFS, you can learn learn from it.

Can before PTA 1003 combined together, substantially the Dijkstra on nothing wrong.

But the disadvantage is that DIjkstra encountered negative weights when the map is very weak, so this time there is a new algorithm.

Guess you like

Origin www.cnblogs.com/veeupup/p/12543659.html