【Shortest Path Algorithm】SPFA

introduce

In the world of computer science, algorithms are like stars in the starry sky, each shining with the light of wisdom. They are silent and firm, like a group of silent philosophers, silently answering the problems of the world.

The steps of the algorithm are like beautiful lines of poetry, allowing complex problems to be released in the flowing characters. They are like clear springs in the mountains, flowing from one peak to another, washing away the dust of problems and revealing the true face.

They are like keys that open the door to computer science. We use them to solve problems, we use them to perform miracles. They are the crystallization of our wisdom, our understanding of the world and our vision for the future.

Walk into this world full of algorithms and feel the light of wisdom and poetic rhythm. Let's explore the unknown territory together, looking for the most beautiful scenery and the most precious treasure.

Algorithms are not only the foundation of computer science, but also the poetry of our lives. They let us see the hope of the future and feel the charm of technology. So, let's embrace algorithms together, let them add color to our lives and bring more possibilities to our world.

Algorithms are like delicacies in computer science, each with its own unique taste and flavor. Some algorithms are like delicate French desserts, complex and delicate, requiring us to taste them one by one patiently; some algorithms are like simple country bread, simple and practical, making people feel cordial and warm.

basic introduction

And in this vast sea of ​​algorithms, there is an algorithm that shines brightly - the shortest path algorithm.

The shortest path algorithm is a graph theory algorithm for finding the shortest path between two nodes in a weighted graph. This algorithm has a wide range of applications in real life, such as:

  1. Traffic planning: The shortest path algorithm can be used in urban traffic planning to help determine the shortest route to reduce traffic congestion and improve traffic efficiency.
  2. Logistics distribution: In logistics distribution, the shortest path algorithm can help determine the shortest path to minimize transportation costs and time.
  3. Network Design: In computer networks, shortest path algorithms can help determine the best route to ensure that data packets are transmitted as quickly as possible.
  4. Geographic Information Systems: In geographic information systems, the shortest path algorithm can be used to determine the shortest path between two points, such as finding the best route between two points on a map.

Among them, SPFA is a key to open the door of graph theory, let us walk into it.

train of thought

The full name of the SPFA algorithm is: Shortest Path Faster Algorithm, which is the name in a paper published by Duan Fanding of Southwest Jiaotong University in 1994. However, Duan Fanding's proof is wrong, and shortly after the Bellman-Ford algorithm was proposed (in 1957), there was queue optimization content , so the SPFA algorithm is not recognized internationally as Duan Fanding's.

In order to avoid the worst case, the more efficient Dijkstra algorithm should be used on the positive weight graph .

If there are negative weight edges in a given graph, algorithms like the Dijkstra algorithm will be useless, and the SPFA algorithm will come in handy. For the sake of brevity, we agree that there is no negative weight loop in the weighted directed graph G, that is, the shortest path must exist. An array d is used to record the estimated value of the shortest path of each node , and an adjacency list is used to store the graph G. The method we adopt is the dynamic approximation method : set up a first-in-first-out queue to save the nodes to be optimized, take out the head node u each time during optimization, and use the current shortest path estimation value of point u to leave point u The node v pointed to performs a relaxation operation . If the estimated value of the shortest path of point v is adjusted, and point v is not in the current queue, point v will be placed at the end of the queue . In this way, nodes are continuously taken out from the queue to perform relaxation operations until the queue is empty.

Theorem: As long as the shortest path exists, the above SPFA algorithm must be able to find the minimum value . Proof: Every time a point is put into the tail of the queue, it is achieved through a relaxation operation . In other words, each optimization will have the shortest path estimation value d[v] of a certain point v become smaller. So the execution of the algorithm will make d smaller and smaller. Since we assume that there are no negatively weighted cycles in the graph, each node has a shortest path value. Therefore, the algorithm will not be executed indefinitely. As the value of d decreases gradually, the algorithm ends when the shortest path value is reached, and the estimated value of the shortest path at this time is the shortest path value of the corresponding node.

In fact, if a point enters the queue up to n times, it indicates that there is a negative cycle in the graph, and there is no shortest path.

The complexity proof (O(kE), k is a small constant) in Duan Fanding's thesis is wrong and is omitted here. The worst time complexity of this algorithm is O(VE).

A very intuitive understanding of SPFA is transformed from the BFS of the unweighted graph. In an unweighted graph, the path experienced by the vertex first reached by BFS must be the shortest path (that is, the minimum number of vertices passed), so at this time, using an array to record node access can make each vertex enter the queue only once, but in the band In the right graph, the path calculated by the first reached vertex is not necessarily the shortest path. One solution is to abandon the array. The time required at this time is naturally exponential , so we cannot abandon the array, but directly update the optimal path when processing a vertex that is already in the queue and the current path is better than the original. solution .

The SPFA algorithm has four optimization strategies: heap optimization, stack optimization, SLF and LLL.

  • Heap optimization: replace the queue with a heap. The difference from Dijkstra is that a point is allowed to enter the heap multiple times. Graphs with negatively weighted edges can get stuck into exponential complexity.

  • Stack optimization: replace the queue with a stack (that is, change the original BFS process into  DFS ), which may be more efficient when looking for negative rings, but the worst time complexity is still exponential.

  • SLF: Small Label First strategy, assuming that the node to be added is j, and the head element of the team is i, if dist(j)<dist(i), then insert j into the head of the team, otherwise insert it into the tail of the team;

  • LLL: Large Label Last strategy, set the first element of the queue as i, the average value of all dist values ​​in the queue is x, if dist(i)>x, insert i into the end of the queue, search for the next element until a certain i is found If dist(i)<=x, i will be dequeued for relaxation operation.

SLF and LLL optimizations perform well on random data, but the worst case is O(VE) on positive weight graphs, and the worst case is exponential complexity on negative weight graphs.

In the final analysis, SPFA is Kuansou, just look at it.

#include <bits/stdc++.h>
using namespace std;
struct edge{int x, y, c, pre;} a[410000];int alen, last[11100];
void ins(int x, int y, int c)//ins函数的功能是建立一条从x出发到y且长度为c的边
{
    a[++alen] = edge{x, y, c, last[x]}; 全局增加一条有向边,并赋值
    last[x] = alen;                     //建立边与边的联系(都是从x出发)
}

int n, m, d[11100]; //d[i]表示目前i和出发点的最短距离
bool v[11100];      //v[i]等于true表示点i在更新队列中,等于false表示点i不在更新队列中
void spfa()
{
    memset(d, 63, sizeof(d));d[1] = 0; //初始化d数组,出发点为1,出发点到自己的距离为0
    memset(v, 0, sizeof(v)); v[1] = 1; //初始化v数组,v[1]等于1,表示出发点1进入队列
    deque<int> Q; Q.push_back(1);   //定义更新队列,出发点进入更新队列
    while (!Q.empty()) //只要队列不为空,就表示还有点等着更新别的点
    {
        int x = Q.front();                     //从队列中取出准备好更新别的点的点x
        for (int k = last[x]; k; k = a[k].pre) //重点理解!k首相等于和x相连的最后一条边的编号。那么倒数第二条和x相连的边的编号是多少呢?在a[k].next可以找到
        {
            int y = a[k].y;
            if (d[y] > d[x] + a[k].c) //尝试用x的最短距离更新y的最短距离
            {
                d[y] = d[x] + a[k].c; //如果点被更新了,那么它马上有冲动(想进更新队列)要去更新它的"亲朋好友"(与之直接相连的点)
                if (v[y] == 0)        //如果点y不在队列,则进入队列;如果已经在队列了,则不用再进入。
                {
                    Q.push_back(y);
                    v[y] = 1;
                }
            }
        }
        Q.pop_front(); //此时x已经更新完它的"亲朋好友",完成使命,退出队列Q
        v[x] = 0;      //标记x已经不再队列,以后x有可能会再次进入队列
    }
    //队列没有点等着更新别的点了,那么意味着所有点的d值都是最优的
    if (d[n] == d[0]) printf("-1\n"); //d[0]为初始的最大值,d[n]等于d[0]表示点n没有被更新,即点n无法到达。
    else              printf("%d", d[n]);
}
int main()
{
    scanf("%d%d", &n, &m);
    alen = 0;
    memset(last, 0, sizeof(last)); //注意构图之前一定要初始化,不然后果很严重!
    for (int i = 1; i <= m; i++)
    {
        int x, y, c;
        scanf("%d%d%d", &x, &y, &c); //题目给出的是无向边,而我们的边目录是有向边
        ins(x, y, c);
        ins(y, x, c); //建立正向边、反向边
    }
    spfa();
    return 0;
}

Detailed comments are written in the code, so look at it.

example 

Don't let it go, go to Luogu to check it yourself.

 It is said that a behavior that deserves a beating needs a picture to comfort it.

Guess you like

Origin blog.csdn.net/aliyonghang/article/details/131152177