Shortest path template-dijkstra algorithm

dijkstra algorithm

The single-source shortest path of a directed weighted graph is suitable for finding the shortest path from all points to a certain vertex i.
Scope of application : graphs where all edge weights are positive numbers. And allow the existence of loops.
Divided into two kinds of naive dijkstra algorithm and heap optimization dijkstra algorithm .

The idea of ​​dijkstra algorithm:
divide the vertices into two categories: the vertices of the shortest path have been countedCollectionsAnd the vertices of the shortest path have not been counted.
Using the greedy idea, every time the vertex j closest to i is never found in the vertices of the set s, it is added to s, and then the distance from the point that can be reached from j to the vertex i is updated, taking the minimum value , and so looping n times , N vertices are all added to the set s.

The algorithmic difference between the naive algorithm and the heap optimization algorithm is that the vertex j closest to i is never found among the vertices of the set s.

The naive algorithm is suitable for dense graphs, using adjacency matrix storage, and the time complexity is O(n 2 )

g[i][j]=x, Which means that the distance i->j is x

The heap optimization algorithm is suitable for sparse graphs, using adjacency table storage, and the time complexity is O(mlogn)

n number of points m number of sides

Analysis : the influence of heavy side and self-loop on dijkstra algorithm

Loop.
Self-loop: the situation from self to self; and the loop
itself will definitely not update to itself, but will only be updated to itself by others, because when you update the distance to other points, you have already joined the set s and become already counted. The point of the shortest distance is invalid, no matter whether it is updated or not.
A loop is a general case of a self-loop. If there is a loop in the graph, according to Dijkstra's idea, he will not go through the loop. When reaching the last node of the loop, it means that the previous node has been added to the set s. , Will no longer be in the considered category, and will not update the distance, because only the cases that are not in the set s will be considered

Heavy edges are easy to handle. When there are multiple edges between two points, just take the shortest edge.

Title description

Given a directed graph with n points and m edges, there may be multiple edges and self-loops in the graph, and all edge weights are positive.

Please find the shortest distance from point 1 to point n. If you cannot walk from point 1 to point n, output -1.

Input format The
first line contains integers n and m.

Each of the next m rows contains three integers x, y, z, indicating that there is a directed edge from point x to point y, and the length of the side is z.

Output format
Output an integer, which meansThe shortest distance from point 1 to point n

If the path does not exist, -1 is output.

Input sample:

3 3
1 2 2
2 3 1
1 3 4

Sample output:

3

Naive dijkstra algorithm

The data range is
1≤n≤500,
1≤m≤10 5 ,
and the length of the side involved in the figure does not exceed 10,000.

Analysis:
n vertices, at most n×(n-1) directed graphs) <don't consider heavy edges and self-loops> , n 2 is equal to 2.5e5, m is equal to 1e5, m and n 2 are one level, which is a dense graph. Adopt adjacency matrix storage.

#include <iostream>
#include <cstring>

using namespace std;

const int N=510,INF=0x3f3f3f3f;
int g[N][N];
int dis[N];
bool vis[N]; //判断该结点是否已加入统计完最短路径的集合s,初始时为false,均未加入
int n,m;//n个点 m条边

void dijkstra(int s)//处理 1号结点->所有结点 的最短路径
{
    
    
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;  //s到自身距离为0
    //最多循环n次,每次选出一个距离1号结点最近的结点,可求出每个结点到s结点的距离
    //第一次选出的一定是s结点自己
    int num=n;
    while (num--) {
    
    
        int t=-1; //最终选出的t号结点,先初始化为小于1的结点(因为正常的结点是从1号开始)
        for (int i=1;i<=n;i++) {
    
    
            if (!vis[i] && (t==-1 || dis[i]<dis[t])) t=i;
        }
        
        if (t==n) break; 
        这题特定的结束条件:我们只需求1->n结点的最短路径。
        此时到了n号结点,但是仍不能确定dis[t]的值是不是无穷大,设想一下:n个顶点,0条边的情况。
        
        vis[t]=true;
        //接下来更新 s->其它结点 通过t结点的最短距离
        for (int i=1;i<=n;i++) {
    
    
            if (!vis[i]) {
    
     只需更新不在集合中的结点,但是if条件可省略
                dis[i]=min(dis[i],dis[t]+g[t][i]);
            }  若i是已经记录过最短路径的点,因为t后于i加入集合,所以dis[i]<=dis[t]必然成立(贪心),所以这里不需要!vis[j]的判断
        }
    }
}

int main()
{
    
    
    scanf("%d%d",&n,&m);
    //初始化邻接矩阵,规定自身到自身距离为0,但是求最短路时自环的边无效,可以不用初始化。
    memset(g,0x3f,sizeof g);
    //存储边
    int a,b,w;
    while (m--) {
    
    
        scanf("%d%d%d",&a,&b,&w);
        g[a][b]=min(g[a][b],w);//处理重边的情况,取最小值的边,同时忽略自环
    }
    dijkstra(1); //求1号点到所有点的最短距离。
    dis[n]==INF?cout<<"-1":cout<<dis[n]; //输出1号顶点到n号顶点的最短距离
    
    return 0;
}

The dis array stores the sum of the shortest paths from the s node to all nodes. It is based on the idea of ​​greed and selects a node that is closest to the s node each time.
Therefore, in the while(num–)num cycle, the value of the dis[] array selected each time increases in turn.

So in the 31st line to the 34th line, the if (!vis[i]) { dis[i]=min(dis[i],dis[t]+g[t][i]);}if judgment can be added or not.
Because if the node i is a node that has been added to the set s, then there must be dis[i] <= dis[t], and the weights of the edges in the graph are all positive numbers, so dis[i] will not be affected of.

Heap optimization dijkstra algorithm

The heap optimization algorithm is to optimize the above 21 22 23 lines, using the smallest heap, each time the vertex with the smallest distance is directly taken out without going through n cycles to determine.

The information that needs to be recorded in the heap is sorted by node and distance, and then sorted by distance. Therefore pair<int,int>, the first one stores the distance and the second one stores the vertex number. After taking it out, it is necessary to update the distance. After the updated distance, the dis array must be modified, but there is another problem : if the node already exists in the heap, it will involve the modification of the heap again. Two keywords. Modification of the heap The heap of the stl container cannot be realized. Although it can be realized by handwriting the heap, it is more complicated.

Therefore, we do not modify it and insert it into the heap again. This approach is that we do not need to write the heap, but the elements in the heap are redundant. There may be multiple elements with the same vertex but different distances in the heap. The original heap There may be at most N elements (because there are at most N vertices), and there may be more now, but this has no effect. Each vertex will only update the distance once according to its shortest distance. You can judge whether the vertex is based on the vis array It is not already added to the set of known shortest paths to avoid duplication.

Another point to note: In the
naive approach, the shortest distance from node i to all other nodes can be determined by looping at most n times.
The theory of heap optimization should be the same, but we replaced the modification operation with the insert operation, so that means that a node may be repeated several times, although only the shortest distance will be effective, and then it will be filtered through the vis array. If there is an unreachable vertex INF, it will be placed behind all vertices.
and so,There are two termination conditions for heap optimization: the queue is not empty || The number of true counts in the vis array is less than n

Data range:
1≤n,m≤1.5×10 5 , the
length of the side in the figure is not less than 0 and not more than 10,000.

m and n are a level, it is a sparse graph, using adjacency table storage, heap optimization.

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int,int> PII; //first表示距离,second表示节点编号,按距离升序排列,每次选出最小的
priority_queue< PII,vector<PII>,greater<PII> >heap;
const int N=2e5,INF=0x3f3f3f3f;
int h[N],e[N],w[N],ne[N],idx;
int dis[N];
bool vis[N];
int n,m;

void add(int a,int b,int c)
{
    
    
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void dijkstra(int s)
{
    
    
    memset(dis,0x3f,sizeof dis);
    dis[s]=0;
    
    heap.push({
    
    dis[s],s});
    int num=n;   n在后面还要用到,不能修改,因此用num替代
    while(heap.size() && num) {
    
     //当队列不空时 或者 成功计数小于n次
        PII t=heap.top();
        heap.pop();
        //每次取出距离s最近的结点,然后开始更新距离
        int v=t.second,d=t.first;   //s->v的最短距离为d
        if (vis[v]) continue; //已经更新过,重复更新的情况跳过,否则
        vis[v]=true,num--;//还剩下n个点未统计
        for (int i=h[v];i!=-1;i=ne[i]) {
    
    
            int j=e[i];
            if (!vis[j] && dis[j]>d+w[i]) {
    
     //同理,if条件可省略
                dis[j]=d+w[i];
                heap.push({
    
    dis[j],j});
            }
        }
    }
}

int main()
{
    
    
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    int a,b,c;
    while (m--) {
    
    
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    dijkstra(1);
    dis[n]==INF?puts("-1"):printf("%d",dis[n]);
    
    return 0;
}

In line 25, only the starting point s is put into the heap, and the remaining vertices should be put in, and then updated, but the heap in stl cannot be updated by keyword, but insert Instead of updating, the remaining points will not be put in, and will be put in when the update arrives.

Regarding the while judgment on line 27, there are two judgment conditions: while(heap.size() && num)
this is due to the appearance of redundant elements, which makes it impossible to end n cycles. It is possible that the vertices in the heap in the n cycles are the points where the shortest distance has been calculated. This cycle is invalid, so it can only work after n valid cycles . At the same time, the condition heap.size()is to prevent the heap from being null before valid n cycles. Imagine a situation where a non-connected graph: n vertices, With 0 edges, the heap is empty during the second loop, but it still needs to loop, PII t=heap.top();there will be a problem, and TLE will occur.

If it is a handwritten heap that can be updated, then looping n times is enough, and all points can be put into the heap at the beginning.

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/113784559